mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Fix UTF-8 sequence validation when used with array literals.
This commit is contained in:
parent
be470c16fe
commit
82106d4655
@ -1229,6 +1229,15 @@ bool TypeChecker::visit(ForStatement const& _forStatement)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeChecker::visit(Return const& _return)
|
||||
{
|
||||
if (_return.expression() && _return.annotation().functionReturnParameters)
|
||||
for (auto parameter: _return.annotation().functionReturnParameters->parameters())
|
||||
if (parameter)
|
||||
_return.expression()->annotation().resultExpectedTypes.push_back(parameter->type());
|
||||
return true;
|
||||
}
|
||||
|
||||
void TypeChecker::endVisit(Return const& _return)
|
||||
{
|
||||
ParameterList const* params = _return.annotation().functionReturnParameters;
|
||||
@ -1342,9 +1351,13 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
|
||||
for (auto const& variable: variables)
|
||||
if (variable)
|
||||
_statement.initialValue()->annotation().resultExpectedTypes.push_back(variable->type());
|
||||
|
||||
// Here we have an initial value and might have to derive some types before we can visit
|
||||
// the variable declaration(s).
|
||||
|
||||
_statement.initialValue()->accept(*this);
|
||||
TypePointers valueTypes;
|
||||
if (auto tupleType = dynamic_cast<TupleType const*>(type(*_statement.initialValue())))
|
||||
@ -1352,7 +1365,6 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
||||
else
|
||||
valueTypes = TypePointers{type(*_statement.initialValue())};
|
||||
|
||||
vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
|
||||
if (variables.empty())
|
||||
// We already have an error for this in the SyntaxChecker.
|
||||
solAssert(m_errorReporter.hasErrors(), "");
|
||||
@ -1645,6 +1657,13 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
||||
if (!components[i])
|
||||
m_errorReporter.fatalTypeError(8381_error, _tuple.location(), "Tuple component cannot be empty.");
|
||||
|
||||
if (TupleExpression const* componentTupleExpression = dynamic_cast<TupleExpression const*>(components[i].get());
|
||||
componentTupleExpression && !_tuple.annotation().resultExpectedTypes.empty() && _tuple.isInlineArray())
|
||||
{
|
||||
solAssert(_tuple.annotation().resultExpectedTypes.size() == 1);
|
||||
componentTupleExpression->annotation().resultExpectedTypes = {dynamic_cast<ArrayType const*>(_tuple.annotation().resultExpectedTypes.front())->baseType()};
|
||||
}
|
||||
|
||||
components[i]->accept(*this);
|
||||
types.push_back(type(*components[i]));
|
||||
|
||||
@ -1665,6 +1684,26 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
||||
{
|
||||
solAssert(!!types[i], "Inline array cannot have empty components");
|
||||
|
||||
if (_tuple.annotation().resultExpectedTypes.size())
|
||||
{
|
||||
solAssert(_tuple.annotation().resultExpectedTypes.size() == 1);
|
||||
ArrayType const* toArray = dynamic_cast<ArrayType const*>(_tuple.annotation().resultExpectedTypes.front());
|
||||
Type const* componentType = type(*components[i]);
|
||||
BoolResult result = componentType->isImplicitlyConvertibleTo(*toArray->baseType());
|
||||
|
||||
if (!result)
|
||||
{
|
||||
std::string const errorMessage =
|
||||
"Type " +
|
||||
componentType->toString(false) +
|
||||
" is not implicitly convertible to expected type " +
|
||||
toArray->baseType()->toString(false) +
|
||||
(result.message().empty() ? "." : ". " + result.message());
|
||||
|
||||
m_errorReporter.typeError(6069_error, components[i]->location(), errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if ((i == 0 || inlineArrayType) && !types[i]->mobileType())
|
||||
m_errorReporter.fatalTypeError(9563_error, components[i]->location(), "Invalid mobile type.");
|
||||
|
||||
@ -1694,7 +1733,13 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
||||
"Type " + inlineArrayType->toString(true) + " is only valid in storage."
|
||||
);
|
||||
|
||||
_tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size());
|
||||
if (_tuple.annotation().resultExpectedTypes.size())
|
||||
{
|
||||
solAssert(_tuple.annotation().resultExpectedTypes.size() == 1);
|
||||
_tuple.annotation().type = TypeProvider::withLocationIfReference(DataLocation::Memory, _tuple.annotation().resultExpectedTypes.front());
|
||||
}
|
||||
else
|
||||
_tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3286,6 +3331,12 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
bool TypeChecker::visit(IndexAccess const& _access)
|
||||
{
|
||||
_access.annotation().isConstant = false;
|
||||
|
||||
if (TupleExpression const* tuple = dynamic_cast<TupleExpression const*>(&_access.baseExpression()))
|
||||
for (auto const& expectedType: _access.annotation().resultExpectedTypes)
|
||||
_access.baseExpression().annotation().resultExpectedTypes.push_back(
|
||||
TypeProvider::array(DataLocation::Memory, expectedType, tuple->components().size()));
|
||||
|
||||
_access.baseExpression().accept(*this);
|
||||
Type const* baseType = type(_access.baseExpression());
|
||||
Type const* resultType = nullptr;
|
||||
@ -3829,6 +3880,7 @@ Declaration const& TypeChecker::dereference(IdentifierPath const& _path) const
|
||||
|
||||
bool TypeChecker::expectType(Expression const& _expression, Type const& _expectedType)
|
||||
{
|
||||
_expression.annotation().resultExpectedTypes = { &_expectedType };
|
||||
_expression.accept(*this);
|
||||
BoolResult result = type(_expression)->isImplicitlyConvertibleTo(_expectedType);
|
||||
if (!result)
|
||||
|
||||
@ -144,6 +144,7 @@ private:
|
||||
void endVisit(TryStatement const& _tryStatement) override;
|
||||
bool visit(WhileStatement const& _whileStatement) override;
|
||||
bool visit(ForStatement const& _forStatement) override;
|
||||
bool visit(Return const& _return) override;
|
||||
void endVisit(Return const& _return) override;
|
||||
void endVisit(EmitStatement const& _emit) override;
|
||||
void endVisit(RevertStatement const& _revert) override;
|
||||
|
||||
@ -287,6 +287,9 @@ struct ExpressionAnnotation: ASTAnnotation
|
||||
/// Note that even the simplest expressions, like `(f)()`, result in an indirect call even if they consist of
|
||||
/// values known at compilation time.
|
||||
bool calledDirectly = false;
|
||||
|
||||
// Types that expression result is assigned to.
|
||||
std::vector<Type const*> resultExpectedTypes;
|
||||
};
|
||||
|
||||
struct IdentifierAnnotation: ExpressionAnnotation
|
||||
|
||||
@ -368,11 +368,20 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
|
||||
if (_tuple.isInlineArray())
|
||||
{
|
||||
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
|
||||
u256 memorySize = max(u256(32u), u256(_tuple.components().size() * arrayType.baseType()->memoryHeadSize()));
|
||||
|
||||
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
|
||||
utils().allocateMemory(max(u256(32u), arrayType.memoryDataSize()));
|
||||
if (arrayType.isDynamicallySized())
|
||||
memorySize += 32;
|
||||
|
||||
utils().allocateMemory(memorySize);
|
||||
m_context << Instruction::DUP1;
|
||||
|
||||
if (arrayType.isDynamicallySized())
|
||||
{
|
||||
m_context << u256(_tuple.components().size());
|
||||
utils().storeInMemoryDynamic(*TypeProvider::uint256());
|
||||
}
|
||||
|
||||
for (auto const& component: _tuple.components())
|
||||
{
|
||||
acceptAndConvert(*component, *arrayType.baseType(), true);
|
||||
|
||||
@ -495,7 +495,6 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
|
||||
if (_tuple.isInlineArray())
|
||||
{
|
||||
auto const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
|
||||
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
|
||||
define(_tuple) <<
|
||||
m_utils.allocateMemoryArrayFunction(arrayType) <<
|
||||
"(" <<
|
||||
@ -503,6 +502,19 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
|
||||
")\n";
|
||||
|
||||
string mpos = IRVariable(_tuple).part("mpos").name();
|
||||
|
||||
if (arrayType.isDynamicallySized())
|
||||
{
|
||||
appendCode() << "// dupa\n";
|
||||
appendCode() <<
|
||||
m_utils.writeToMemoryFunction(*TypeProvider::uint256()) <<
|
||||
"(" <<
|
||||
(mpos + ", " + to_string(_tuple.components().size())) <<
|
||||
")\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
Type const& baseType = *arrayType.baseType();
|
||||
for (size_t i = 0; i < _tuple.components().size(); i++)
|
||||
{
|
||||
@ -510,10 +522,15 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
|
||||
component.accept(*this);
|
||||
setLocation(_tuple);
|
||||
IRVariable converted = convert(component, baseType);
|
||||
|
||||
string position =
|
||||
mpos + ", " + to_string(i * arrayType.memoryStride() +
|
||||
(arrayType.isDynamicallySized() ? 32 : 0));
|
||||
|
||||
appendCode() <<
|
||||
m_utils.writeToMemoryFunction(baseType) <<
|
||||
"(" <<
|
||||
("add(" + mpos + ", " + to_string(i * arrayType.memoryStride()) + ")") <<
|
||||
("add(" + position + ")") <<
|
||||
", " <<
|
||||
converted.commaSeparatedList() <<
|
||||
")\n";
|
||||
|
||||
@ -16,7 +16,7 @@ contract C {
|
||||
}
|
||||
// ----
|
||||
// constructor()
|
||||
// gas irOptimized: 237351
|
||||
// gas irOptimized: 224415
|
||||
// gas legacy: 221315
|
||||
// gas legacyOptimized: 185247
|
||||
// gas legacyOptimized: 208995
|
||||
// f() -> 0
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
contract C {
|
||||
function externalDefault() external returns(uint) { return 11; }
|
||||
function externalView() external view returns(uint) { return 12; }
|
||||
function externalPure() external pure returns(uint) { return 13; }
|
||||
|
||||
function internalDefault() internal returns(uint) { return 21; }
|
||||
function internalView() internal view returns(uint) { return 22; }
|
||||
function internalPure() internal pure returns(uint) { return 23; }
|
||||
|
||||
@ -41,9 +39,6 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 7407: (760-779): Type function () view external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory.
|
||||
// TypeError 7407: (812-826): Type function () view returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory.
|
||||
// TypeError 7407: (1230-1249): Type function () pure external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory.
|
||||
// TypeError 7407: (1282-1296): Type function () pure returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory.
|
||||
// TypeError 7407: (1688-1707): Type function () pure external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory.
|
||||
// TypeError 7407: (1737-1751): Type function () pure returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory.
|
||||
// testViewToDefault() -> 12, 22
|
||||
// testPureToDefault() -> 13, 23
|
||||
// testPureToView() -> 13, 23
|
||||
@ -0,0 +1,41 @@
|
||||
contract C {
|
||||
function externalView() external view returns(uint) { return 12; }
|
||||
function externalPure() external pure returns(uint) { return 13; }
|
||||
|
||||
function internalView() internal view returns(uint) { return 22; }
|
||||
function internalPure() internal pure returns(uint) { return 23; }
|
||||
|
||||
function testViewToDefault() public returns (uint, uint) {
|
||||
function () external returns(uint) externalDefault;
|
||||
function () internal returns(uint) internalDefault;
|
||||
|
||||
externalDefault = this.externalView;
|
||||
internalDefault = internalView;
|
||||
|
||||
return (externalDefault(), internalDefault());
|
||||
}
|
||||
|
||||
function testPureToDefault() public returns (uint, uint) {
|
||||
function () external returns(uint) externalDefault;
|
||||
function () internal returns(uint) internalDefault;
|
||||
|
||||
externalDefault = this.externalPure;
|
||||
internalDefault = internalPure;
|
||||
|
||||
return (externalDefault(), internalDefault());
|
||||
}
|
||||
|
||||
function testPureToView() public returns (uint, uint) {
|
||||
function () external view returns(uint) externalView;
|
||||
function () internal view returns(uint) internalView;
|
||||
|
||||
externalView = this.externalPure;
|
||||
internalView = internalPure;
|
||||
|
||||
return (externalView(), internalView());
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// testViewToDefault() -> 12, 22
|
||||
// testPureToDefault() -> 13, 23
|
||||
// testPureToView() -> 13, 23
|
||||
@ -20,6 +20,6 @@ contract C {
|
||||
|
||||
// ----
|
||||
// g() -> 2, 6
|
||||
// gas irOptimized: 178637
|
||||
// gas irOptimized: 178634
|
||||
// gas legacy: 180945
|
||||
// gas legacyOptimized: 179460
|
||||
|
||||
@ -6,4 +6,9 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 6069: (123-126): Type literal_string "h" is not implicitly convertible to expected type string calldata.
|
||||
// TypeError 6069: (128-131): Type literal_string "e" is not implicitly convertible to expected type string calldata.
|
||||
// TypeError 6069: (133-136): Type literal_string "l" is not implicitly convertible to expected type string calldata.
|
||||
// TypeError 6069: (138-141): Type literal_string "l" is not implicitly convertible to expected type string calldata.
|
||||
// TypeError 6069: (143-146): Type literal_string "o" is not implicitly convertible to expected type string calldata.
|
||||
// TypeError 6359: (122-147): Return argument type string memory[5] memory is not implicitly convertible to expected type (type of first return variable) string calldata[5] calldata.
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
string[2] memory a2 = [string(bytes(hex'74000001')), string(bytes(hex'c0a80101'))];
|
||||
string[2] memory a1 = [hex'74000001', hex'c0a80101'];
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 6069: (182-195): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.
|
||||
@ -0,0 +1,7 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
string memory s = [hex'74000001', hex'c0a80101'][1];
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 6069: (86-99): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.
|
||||
@ -0,0 +1,5 @@
|
||||
contract C {
|
||||
string[2] data = [hex'74000001', hex'c0a80101'];
|
||||
}
|
||||
// ----
|
||||
// TypeError 6069: (50-63): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string storage ref. Contains invalid UTF-8 sequence at position 4.
|
||||
@ -4,4 +4,6 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 6069: (71-76): Type literal_string "foo" is not implicitly convertible to expected type uint256.
|
||||
// TypeError 6069: (78-82): Type bool is not implicitly convertible to expected type uint256.
|
||||
// TypeError 6378: (66-83): Unable to deduce common type for array elements.
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
string[2][2] memory a1 = [['foo', 'bar'], [hex'74000001', hex'c0a80101']];
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 6069: (110-123): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.
|
||||
@ -0,0 +1,7 @@
|
||||
contract C {
|
||||
function f() public pure {
|
||||
string[2] memory s = [['foo', 'bar'], [hex'74000001', hex'c0a80101']][1];
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 6069: (106-119): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.
|
||||
@ -0,0 +1,11 @@
|
||||
contract C {
|
||||
function f() public pure returns (string[2] memory) {
|
||||
return [string(bytes(hex'74000001')), string(bytes(hex'c0a80101'))];
|
||||
}
|
||||
|
||||
function g() public pure returns (string[2] memory) {
|
||||
return [hex'74000001', hex'c0a80101'];
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 6069: (244-257): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.
|
||||
@ -0,0 +1,7 @@
|
||||
contract C {
|
||||
function g() public pure returns (string[2][2] memory) {
|
||||
return [['foo', 'bar'], [hex'74000001', hex'c0a80101']];
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 6069: (122-135): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.
|
||||
Loading…
Reference in New Issue
Block a user