Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 3 additions & 33 deletions libsolidity/analysis/DeclarationTypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@
// SPDX-License-Identifier: GPL-3.0

#include <libsolidity/analysis/DeclarationTypeChecker.h>

#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/analysis/TypeChecker.h>

#include <libsolidity/ast/TypeProvider.h>

#include <liblangutil/ErrorReporter.h>

#include <libsolutil/Algorithms.h>
#include <libsolutil/Visitor.h>

#include <range/v3/view/transform.hpp>

Expand Down Expand Up @@ -335,36 +333,8 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)

if (Expression const* length = _typeName.length())
{
std::optional<rational> lengthValue;
if (length->annotation().type && length->annotation().type->category() == Type::Category::RationalNumber)
lengthValue = dynamic_cast<RationalNumberType const&>(*length->annotation().type).value();
else if (std::optional<ConstantEvaluator::TypedRational> value = ConstantEvaluator::evaluate(m_errorReporter, *length))
lengthValue = value->value;

if (!lengthValue)
m_errorReporter.typeError(
5462_error,
length->location(),
"Invalid array length, expected integer literal or constant expression."
);
else if (*lengthValue == 0)
m_errorReporter.typeError(1406_error, length->location(), "Array with zero length specified.");
else if (lengthValue->denominator() != 1)
m_errorReporter.typeError(3208_error, length->location(), "Array with fractional length specified.");
else if (*lengthValue < 0)
m_errorReporter.typeError(3658_error, length->location(), "Array with negative length specified.");
else if (lengthValue > TypeProvider::uint256()->max())
m_errorReporter.typeError(
1847_error,
length->location(),
"Array length too large, maximum is 2**256 - 1."
);

_typeName.annotation().type = TypeProvider::array(
DataLocation::Storage,
baseType,
lengthValue ? u256(lengthValue->numerator()) : u256(0)
);
auto const lengthValue = TypeChecker::checkArrayLengthExpression(*length, m_errorReporter);
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue);
}
else
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType);
Expand Down
102 changes: 81 additions & 21 deletions libsolidity/analysis/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
*/

#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/analysis/ConstantEvaluator.h>

#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTUtils.h>
#include <libsolidity/ast/UserDefinableOperators.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/ast/UserDefinableOperators.h>

#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
Expand All @@ -36,9 +38,7 @@
#include <libsolutil/Algorithms.h>
#include <libsolutil/StringUtils.h>
#include <libsolutil/Views.h>
#include <libsolutil/Visitor.h>

#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/predicate.hpp>

#include <fmt/format.h>
Expand Down Expand Up @@ -3470,29 +3470,56 @@ bool TypeChecker::visit(IndexAccess const& _access)
case Type::Category::TypeType:
{
TypeType const& typeType = dynamic_cast<TypeType const&>(*baseType);
if (auto const* contractType = dynamic_cast<ContractType const*>(typeType.actualType()))
auto const actualType = typeType.actualType();
auto const actualTypeCategory = actualType->category();

switch (actualTypeCategory)
{
case Type::Category::Contract:
{
auto const* contractType = reinterpret_cast<ContractType const*>(actualType);
if (contractType->contractDefinition().isLibrary())
m_errorReporter.typeError(2876_error, _access.location(), "Index access for library types and arrays of libraries are not possible.");
if (!index)
resultType = TypeProvider::typeType(TypeProvider::array(DataLocation::Memory, typeType.actualType()));
else
{
u256 length = 1;
if (expectType(*index, *TypeProvider::uint256()))
else if (contractType->isSuper())
{
if (auto indexValue = dynamic_cast<RationalNumberType const*>(type(*index)))
length = indexValue->literalValue(nullptr);
else
m_errorReporter.fatalTypeError(3940_error, index->location(), "Integer constant expected.");
m_errorReporter.typeError(
5530_error,
_access.location(),
"Index notation is not allowed for type.");
}
else
solAssert(m_errorReporter.hasErrors(), "Expected errors as expectType returned false");
break;
}
case Type::Category::Address:
case Type::Category::Integer:
case Type::Category::FixedBytes:
case Type::Category::Struct:
case Type::Category::Array:
case Type::Category::Enum:
case Type::Category::UserDefinedValueType:
case Type::Category::Bool:
break;
case Type::Category::Mapping:
case Type::Category::RationalNumber:
case Type::Category::StringLiteral:
case Type::Category::FixedPoint:
case Type::Category::ArraySlice:
case Type::Category::Function:
case Type::Category::Tuple:
case Type::Category::TypeType:
case Type::Category::Modifier:
case Type::Category::Magic:
case Type::Category::Module:
case Type::Category::InaccessibleDynamic:
solAssert(false, "Unexpected actual type category of TypeType.");
}

resultType = TypeProvider::typeType(TypeProvider::array(
DataLocation::Memory,
typeType.actualType(),
length
));
if (!index)
resultType = TypeProvider::typeType(TypeProvider::array(DataLocation::Memory, actualType));
else
{
index->accept(*this);
auto const lengthValue = checkArrayLengthExpression(*index, m_errorReporter);
resultType = TypeProvider::typeType(TypeProvider::array(DataLocation::Memory, actualType, lengthValue));
}
break;
}
Expand Down Expand Up @@ -4239,3 +4266,36 @@ bool TypeChecker::useABICoderV2() const
return *m_currentSourceUnit->annotation().useABICoderV2;

}

u256 TypeChecker::checkArrayLengthExpression(Expression const& _arrayLengthExpression, ErrorReporter& _errorReporter)
{
std::optional<rational> lengthValue;
if (_arrayLengthExpression.annotation().type && _arrayLengthExpression.annotation().type->category() == Type::Category::RationalNumber)
lengthValue = dynamic_cast<RationalNumberType const&>(*_arrayLengthExpression.annotation().type).value();
else if (std::optional<ConstantEvaluator::TypedRational> value = ConstantEvaluator::evaluate(_errorReporter, _arrayLengthExpression))
lengthValue = value->value;

if (!lengthValue)
_errorReporter.typeError(
5462_error,
_arrayLengthExpression.location(),
"Invalid array length, expected integer literal or constant expression."
);
else if (*lengthValue == 0)
_errorReporter.typeError(1406_error, _arrayLengthExpression.location(), "Array with zero length specified.");
else if (lengthValue->denominator() != 1)
_errorReporter.typeError(3208_error, _arrayLengthExpression.location(), "Array with fractional length specified.");
else if (*lengthValue < 0)
_errorReporter.typeError(3658_error, _arrayLengthExpression.location(), "Array with negative length specified.");
else if (lengthValue > TypeProvider::uint256()->max())
_errorReporter.typeError(
1847_error,
_arrayLengthExpression.location(),
"Array length too large, maximum is 2**256 - 1."
);
else
return u256(lengthValue->numerator());

solAssert(lengthValue || _errorReporter.hasErrors(), "Must have reported errors.");
return 0;
}
4 changes: 4 additions & 0 deletions libsolidity/analysis/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class TypeChecker: private ASTConstVisitor

static bool typeSupportedByOldABIEncoder(Type const& _type, bool _isLibraryCall);

/// Checks that `_arrayLengthExpression` is allowed for array length value.
/// @returns a value of length or report an error.
static u256 checkArrayLengthExpression(Expression const& _arrayLengthExpression, langutil::ErrorReporter& _errorReporter);

private:

bool visit(ContractDefinition const& _contract) override;
Expand Down
5 changes: 2 additions & 3 deletions test/libsolidity/syntaxTests/array/contract_index_access.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
contract C {
function f() view public {
C[0];
function f() pure public {
C[1];
}
}
// ----
// Warning 6133: (52-56): Statement has no effect.
// Warning 2018: (17-63): Function state mutability can be restricted to pure
9 changes: 9 additions & 0 deletions test/libsolidity/syntaxTests/array/interface_index_access.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
interface I {}

library L {
function f() pure public {
I[1];
}
}
// ----
// Warning 6133: (67-71): Statement has no effect.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
contract C {
function f0() public {
abi.decode("", (int[0]));
}

function f1() public {
abi.decode("", (int[0][]));
}

function f2() public {
abi.decode("", (int[][0]));
}

function f3() public {
abi.decode("", (int[][0][]));
}

function f4() public {
abi.decode("", (int[][][0]));
}

struct S { uint t; }
function f5() public {
abi.decode("", (S[][][0]));
}
}
// ----
// TypeError 1406: (68-69): Array with zero length specified.
// TypeError 1406: (136-137): Array with zero length specified.
// TypeError 1406: (208-209): Array with zero length specified.
// TypeError 1406: (278-279): Array with zero length specified.
// TypeError 1406: (352-353): Array with zero length specified.
// TypeError 1406: (447-448): Array with zero length specified.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
contract C {
function f() public returns (bytes memory) {
return abi.encode(abi.decode("", (int[0])));
}
}
// ----
// TypeError 1406: (108-109): Array with zero length specified.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
contract C {
function f() view public {
C[0];
}
}
// ----
// TypeError 1406: (54-55): Array with zero length specified.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
interface I {}

contract C {
function f() view public {
I[0];
}
}
// ----
// TypeError 1406: (70-71): Array with zero length specified.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
library C {
library L {
function f() view public {
C[0];
L[1];
}
}
// ----
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
library L {
function f() view public {
L[0];
}
}
// ----
// TypeError 2876: (51-55): Index access for library types and arrays of libraries are not possible.
// TypeError 1406: (53-54): Array with zero length specified.
10 changes: 10 additions & 0 deletions test/libsolidity/syntaxTests/array/invalid/super_index_access.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract B {
}

contract C is B {
function f(bool[] calldata dd) pure public {
super[1];
}
}
// ----
// TypeError 5530: (91-99): Index notation is not allowed for type.
Original file line number Diff line number Diff line change
@@ -1,15 +1,53 @@
struct S { uint[0] x; }

error Err(uint[0]);
event Ev(uint[0]);

contract C {
int[0] a;
uint[0] b;
bytes1[0] c;
bytes32[0] d;
bytes[0] e;
string[0] f;
int[0] a;
uint[0] b;
bytes1[0] c;
bytes32[0] d;
bytes[0] e;
string[0] f;
mapping(int => int[0]) m;
function() returns (uint[0] memory) fPtr;

function foo () private pure {
uint[0] storage x;
}

function foo (uint[0] memory fArg) private pure returns(uint[0] memory) {
fArg;
}

function tup() private pure {
(uint[0] memory aTup, uint bTup) = ([], 1);
}

function cat() private pure {
try this.g() returns (uint[0] memory) {} catch (bytes memory bCatch) {}
}

function array_alloc() private pure {
new uint[0][](3);
}
}
// ----
// TypeError 1406: (19-20): Array with zero length specified.
// TypeError 1406: (32-33): Array with zero length specified.
// TypeError 1406: (47-48): Array with zero length specified.
// TypeError 1406: (63-64): Array with zero length specified.
// TypeError 1406: (77-78): Array with zero length specified.
// TypeError 1406: (92-93): Array with zero length specified.
// TypeError 1406: (16-17): Array with zero length specified.
// TypeError 1406: (40-41): Array with zero length specified.
// TypeError 1406: (59-60): Array with zero length specified.
// TypeError 1406: (86-87): Array with zero length specified.
// TypeError 1406: (101-102): Array with zero length specified.
// TypeError 1406: (118-119): Array with zero length specified.
// TypeError 1406: (136-137): Array with zero length specified.
// TypeError 1406: (152-153): Array with zero length specified.
// TypeError 1406: (169-170): Array with zero length specified.
// TypeError 1406: (198-199): Array with zero length specified.
// TypeError 1406: (234-235): Array with zero length specified.
// TypeError 1406: (300-301): Array with zero length specified.
// TypeError 1406: (344-345): Array with zero length specified.
// TypeError 1406: (386-387): Array with zero length specified.
// TypeError 1406: (469-470): Array with zero length specified.
// TypeError 1406: (583-584): Array with zero length specified.
// TypeError 1406: (694-695): Array with zero length specified.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract C {
function conditional() private pure {
true ? uint[0] : uint[0];
}
}
// ----
// TypeError 1406: (75-76): Array with zero length specified.
// TypeError 1406: (85-86): Array with zero length specified.
// TypeError 9717: (70-77): Invalid mobile type in true expression.
// TypeError 3703: (80-87): Invalid mobile type in false expression.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
contract C {
function others() private pure {
type(uint[0]);
}
}
// ----
// TypeError 1406: (68-69): Array with zero length specified.
// TypeError 4259: (63-70): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(uint256[0] memory) provided.
Loading