mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9883 from ethereum/develop
Merge develop into breaking.
This commit is contained in:
@@ -219,11 +219,12 @@ bool SyntaxChecker::visit(Throw const& _throwStatement)
|
||||
|
||||
bool SyntaxChecker::visit(Literal const& _literal)
|
||||
{
|
||||
if ((_literal.token() == Token::UnicodeStringLiteral) && !validateUTF8(_literal.value()))
|
||||
size_t invalidSequence;
|
||||
if ((_literal.token() == Token::UnicodeStringLiteral) && !validateUTF8(_literal.value(), invalidSequence))
|
||||
m_errorReporter.syntaxError(
|
||||
8452_error,
|
||||
_literal.location(),
|
||||
"Invalid UTF-8 sequence found"
|
||||
"Contains invalid UTF-8 sequence at position " + toString(invalidSequence) + "."
|
||||
);
|
||||
|
||||
if (_literal.token() != Token::Number)
|
||||
|
||||
@@ -1645,7 +1645,8 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
|
||||
dataLoc = argRefType->location();
|
||||
if (auto type = dynamic_cast<ReferenceType const*>(resultType))
|
||||
resultType = TypeProvider::withLocation(type, dataLoc, type->isPointer());
|
||||
if (argType->isExplicitlyConvertibleTo(*resultType))
|
||||
BoolResult result = argType->isExplicitlyConvertibleTo(*resultType);
|
||||
if (result)
|
||||
{
|
||||
if (auto argArrayType = dynamic_cast<ArrayType const*>(argType))
|
||||
{
|
||||
@@ -1716,14 +1717,15 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
|
||||
"you can use the .address member of the function."
|
||||
);
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
m_errorReporter.typeErrorConcatenateDescriptions(
|
||||
9640_error,
|
||||
_functionCall.location(),
|
||||
"Explicit type conversion not allowed from \"" +
|
||||
argType->toString() +
|
||||
"\" to \"" +
|
||||
resultType->toString() +
|
||||
"\"."
|
||||
"\".",
|
||||
result.message()
|
||||
);
|
||||
}
|
||||
if (auto addressType = dynamic_cast<AddressType const*>(resultType))
|
||||
@@ -3217,7 +3219,8 @@ Declaration const& TypeChecker::dereference(UserDefinedTypeName const& _typeName
|
||||
bool TypeChecker::expectType(Expression const& _expression, Type const& _expectedType)
|
||||
{
|
||||
_expression.accept(*this);
|
||||
if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType))
|
||||
BoolResult result = type(_expression)->isImplicitlyConvertibleTo(_expectedType);
|
||||
if (!result)
|
||||
{
|
||||
auto errorMsg = "Type " +
|
||||
type(_expression)->toString() +
|
||||
@@ -3236,17 +3239,23 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
|
||||
errorMsg + ", but it can be explicitly converted."
|
||||
);
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
m_errorReporter.typeErrorConcatenateDescriptions(
|
||||
2326_error,
|
||||
_expression.location(),
|
||||
errorMsg +
|
||||
". Try converting to type " +
|
||||
type(_expression)->mobileType()->toString() +
|
||||
" or use an explicit conversion."
|
||||
" or use an explicit conversion.",
|
||||
result.message()
|
||||
);
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(7407_error, _expression.location(), errorMsg + ".");
|
||||
m_errorReporter.typeErrorConcatenateDescriptions(
|
||||
7407_error,
|
||||
_expression.location(),
|
||||
errorMsg + ".",
|
||||
result.message()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1352,13 +1352,25 @@ StringLiteralType::StringLiteralType(string _value):
|
||||
BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
if (auto fixedBytes = dynamic_cast<FixedBytesType const*>(&_convertTo))
|
||||
return static_cast<size_t>(fixedBytes->numBytes()) >= m_value.size();
|
||||
{
|
||||
if (static_cast<size_t>(fixedBytes->numBytes()) < m_value.size())
|
||||
return BoolResult::err("Literal is larger than the type.");
|
||||
return true;
|
||||
}
|
||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(&_convertTo))
|
||||
{
|
||||
size_t invalidSequence;
|
||||
if (arrayType->isString() && !util::validateUTF8(value(), invalidSequence))
|
||||
return BoolResult::err(
|
||||
"Contains invalid UTF-8 sequence at position " +
|
||||
util::toString(invalidSequence) +
|
||||
"."
|
||||
);
|
||||
return
|
||||
arrayType->location() != DataLocation::CallData &&
|
||||
arrayType->isByteArray() &&
|
||||
!(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer()) &&
|
||||
!(arrayType->isString() && !util::validateUTF8(value()));
|
||||
!(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer());
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -1379,12 +1391,19 @@ bool StringLiteralType::operator==(Type const& _other) const
|
||||
|
||||
std::string StringLiteralType::toString(bool) const
|
||||
{
|
||||
size_t invalidSequence;
|
||||
auto isPrintableASCII = [](string const& s)
|
||||
{
|
||||
for (auto c: s)
|
||||
{
|
||||
if (static_cast<unsigned>(c) <= 0x1f || static_cast<unsigned>(c) >= 0x7f)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!util::validateUTF8(m_value, invalidSequence))
|
||||
return "literal_string (contains invalid UTF-8 sequence at position " + util::toString(invalidSequence) + ")";
|
||||
|
||||
return "literal_string \"" + m_value + "\"";
|
||||
return isPrintableASCII(m_value) ?
|
||||
("literal_string \"" + m_value + "\"") :
|
||||
("literal_string hex\"" + util::toHex(util::asBytes(m_value)) + "\"");
|
||||
}
|
||||
|
||||
TypePointer StringLiteralType::mobileType() const
|
||||
|
||||
@@ -632,6 +632,16 @@ string YulUtilFunctions::overflowCheckedIntExpFunction(
|
||||
|
||||
string YulUtilFunctions::overflowCheckedUnsignedExpFunction()
|
||||
{
|
||||
// Checks for the "small number specialization" below.
|
||||
using namespace boost::multiprecision;
|
||||
solAssert(pow(bigint(10), 77) < pow(bigint(2), 256), "");
|
||||
solAssert(pow(bigint(11), 77) >= pow(bigint(2), 256), "");
|
||||
solAssert(pow(bigint(10), 78) >= pow(bigint(2), 256), "");
|
||||
|
||||
solAssert(pow(bigint(306), 31) < pow(bigint(2), 256), "");
|
||||
solAssert(pow(bigint(307), 31) >= pow(bigint(2), 256), "");
|
||||
solAssert(pow(bigint(306), 32) >= pow(bigint(2), 256), "");
|
||||
|
||||
string functionName = "checked_exp_unsigned";
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
@@ -644,25 +654,35 @@ string YulUtilFunctions::overflowCheckedUnsignedExpFunction()
|
||||
if iszero(exponent) { power := 1 leave }
|
||||
if iszero(base) { power := 0 leave }
|
||||
|
||||
power := 1
|
||||
|
||||
for { } gt(exponent, 1) {}
|
||||
// Specializations for small bases
|
||||
switch base
|
||||
// 0 is handled above
|
||||
case 1 { power := 1 leave }
|
||||
case 2
|
||||
{
|
||||
// overflow check for base * base
|
||||
if gt(base, div(max, base)) { revert(0, 0) }
|
||||
if and(exponent, 1)
|
||||
{
|
||||
// no check needed here because base >= power
|
||||
power := mul(power, base)
|
||||
}
|
||||
base := mul(base, base)
|
||||
exponent := <shr_1>(exponent)
|
||||
if gt(exponent, 255) { revert(0, 0) }
|
||||
power := exp(2, exponent)
|
||||
if gt(power, max) { revert(0, 0) }
|
||||
leave
|
||||
}
|
||||
if or(
|
||||
and(lt(base, 11), lt(exponent, 78)),
|
||||
and(lt(base, 307), lt(exponent, 32))
|
||||
)
|
||||
{
|
||||
power := exp(base, exponent)
|
||||
if gt(power, max) { revert(0, 0) }
|
||||
leave
|
||||
}
|
||||
|
||||
power, base := <expLoop>(1, base, exponent, max)
|
||||
|
||||
if gt(power, div(max, base)) { revert(0, 0) }
|
||||
power := mul(power, base)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("expLoop", overflowCheckedExpLoopFunction())
|
||||
("shr_1", shiftRightFunction(1))
|
||||
.render();
|
||||
});
|
||||
@@ -703,6 +723,35 @@ string YulUtilFunctions::overflowCheckedSignedExpFunction()
|
||||
|
||||
// Below this point, base is always positive.
|
||||
|
||||
power, base := <expLoop>(power, base, exponent, max)
|
||||
|
||||
if and(sgt(power, 0), gt(power, div(max, base))) { revert(0, 0) }
|
||||
if and(slt(power, 0), slt(power, sdiv(min, base))) { revert(0, 0) }
|
||||
power := mul(power, base)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("expLoop", overflowCheckedExpLoopFunction())
|
||||
("shr_1", shiftRightFunction(1))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::overflowCheckedExpLoopFunction()
|
||||
{
|
||||
// We use this loop for both signed and unsigned exponentiation
|
||||
// because we pull out the first iteration in the signed case which
|
||||
// results in the base always being positive.
|
||||
|
||||
// This function does not include the final multiplication.
|
||||
|
||||
string functionName = "checked_exp_helper";
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(_power, _base, exponent, max) -> power, base {
|
||||
power := _power
|
||||
base := _base
|
||||
for { } gt(exponent, 1) {}
|
||||
{
|
||||
// overflow check for base * base
|
||||
@@ -712,16 +761,13 @@ string YulUtilFunctions::overflowCheckedSignedExpFunction()
|
||||
// No checks for power := mul(power, base) needed, because the check
|
||||
// for base * base above is sufficient, since:
|
||||
// |power| <= base (proof by induction) and thus:
|
||||
// |power * base| <= base * base <= max <= |min|
|
||||
// |power * base| <= base * base <= max <= |min| (for signed)
|
||||
// (this is equally true for signed and unsigned exp)
|
||||
power := mul(power, base)
|
||||
}
|
||||
base := mul(base, base)
|
||||
exponent := <shr_1>(exponent)
|
||||
}
|
||||
|
||||
if and(sgt(power, 0), gt(power, div(max, base))) { revert(0, 0) }
|
||||
if and(slt(power, 0), slt(power, sdiv(min, base))) { revert(0, 0) }
|
||||
power := mul(power, base)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
|
||||
@@ -140,6 +140,10 @@ public:
|
||||
/// signature: (base, exponent, min, max) -> power
|
||||
std::string overflowCheckedSignedExpFunction();
|
||||
|
||||
/// Helper function for the two checked exponentiation functions.
|
||||
/// signature: (power, base, exponent, max) -> power
|
||||
std::string overflowCheckedExpLoopFunction();
|
||||
|
||||
/// @returns the name of a function that fetches the length of the given
|
||||
/// array
|
||||
/// signature: (array) -> length
|
||||
|
||||
@@ -352,31 +352,10 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
|
||||
{
|
||||
createExpr(_assignment);
|
||||
|
||||
static set<Token> const compoundOps{
|
||||
Token::AssignAdd,
|
||||
Token::AssignSub,
|
||||
Token::AssignMul,
|
||||
Token::AssignDiv,
|
||||
Token::AssignMod
|
||||
};
|
||||
Token op = _assignment.assignmentOperator();
|
||||
if (op != Token::Assign && !compoundOps.count(op))
|
||||
{
|
||||
Expression const* identifier = &_assignment.leftHandSide();
|
||||
if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(identifier))
|
||||
identifier = leftmostBase(*indexAccess);
|
||||
// Give it a new index anyway to keep the SSA scheme sound.
|
||||
solAssert(identifier, "");
|
||||
if (auto varDecl = identifierToVariable(*identifier))
|
||||
m_context.newValue(*varDecl);
|
||||
solAssert(TokenTraits::isAssignmentOp(op), "");
|
||||
|
||||
m_errorReporter.warning(
|
||||
9149_error,
|
||||
_assignment.location(),
|
||||
"Assertion checker does not yet implement this assignment operator."
|
||||
);
|
||||
}
|
||||
else if (!smt::isSupportedType(*_assignment.annotation().type))
|
||||
if (!smt::isSupportedType(*_assignment.annotation().type))
|
||||
{
|
||||
// Give it a new index anyway to keep the SSA scheme sound.
|
||||
|
||||
@@ -394,9 +373,9 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
|
||||
else
|
||||
{
|
||||
auto const& type = _assignment.annotation().type;
|
||||
auto rightHandSide = compoundOps.count(op) ?
|
||||
compoundAssignment(_assignment) :
|
||||
expr(_assignment.rightHandSide(), type);
|
||||
auto rightHandSide = op == Token::Assign ?
|
||||
expr(_assignment.rightHandSide(), type) :
|
||||
compoundAssignment(_assignment);
|
||||
defineExpr(_assignment, rightHandSide);
|
||||
assignment(
|
||||
_assignment.leftHandSide(),
|
||||
@@ -1388,6 +1367,59 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
|
||||
return {value, valueUnbounded};
|
||||
}
|
||||
|
||||
smtutil::Expression SMTEncoder::bitwiseOperation(
|
||||
Token _op,
|
||||
smtutil::Expression const& _left,
|
||||
smtutil::Expression const& _right,
|
||||
TypePointer const& _commonType
|
||||
)
|
||||
{
|
||||
static set<Token> validOperators{
|
||||
Token::BitAnd,
|
||||
Token::BitOr,
|
||||
Token::BitXor,
|
||||
Token::SHL,
|
||||
Token::SHR,
|
||||
Token::SAR
|
||||
};
|
||||
solAssert(validOperators.count(_op), "");
|
||||
solAssert(_commonType, "");
|
||||
|
||||
auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_commonType);
|
||||
|
||||
auto bvLeft = smtutil::Expression::int2bv(_left, bvSize);
|
||||
auto bvRight = smtutil::Expression::int2bv(_right, bvSize);
|
||||
|
||||
optional<smtutil::Expression> result;
|
||||
switch (_op)
|
||||
{
|
||||
case Token::BitAnd:
|
||||
result = bvLeft & bvRight;
|
||||
break;
|
||||
case Token::BitOr:
|
||||
result = bvLeft | bvRight;
|
||||
break;
|
||||
case Token::BitXor:
|
||||
result = bvLeft ^ bvRight;
|
||||
break;
|
||||
case Token::SHL:
|
||||
result = bvLeft << bvRight;
|
||||
break;
|
||||
case Token::SHR:
|
||||
solAssert(false, "");
|
||||
case Token::SAR:
|
||||
result = isSigned ?
|
||||
smtutil::Expression::ashr(bvLeft, bvRight) :
|
||||
bvLeft >> bvRight;
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
||||
solAssert(result.has_value(), "");
|
||||
return smtutil::Expression::bv2int(*result, isSigned);
|
||||
}
|
||||
|
||||
void SMTEncoder::compareOperation(BinaryOperation const& _op)
|
||||
{
|
||||
auto const& commonType = _op.annotation().commonType;
|
||||
@@ -1464,39 +1496,12 @@ void SMTEncoder::bitwiseOperation(BinaryOperation const& _op)
|
||||
auto commonType = _op.annotation().commonType;
|
||||
solAssert(commonType, "");
|
||||
|
||||
auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(commonType);
|
||||
|
||||
auto bvLeft = smtutil::Expression::int2bv(expr(_op.leftExpression(), commonType), bvSize);
|
||||
auto bvRight = smtutil::Expression::int2bv(expr(_op.rightExpression(), commonType), bvSize);
|
||||
|
||||
optional<smtutil::Expression> result;
|
||||
switch (op)
|
||||
{
|
||||
case Token::BitAnd:
|
||||
result = bvLeft & bvRight;
|
||||
break;
|
||||
case Token::BitOr:
|
||||
result = bvLeft | bvRight;
|
||||
break;
|
||||
case Token::BitXor:
|
||||
result = bvLeft ^ bvRight;
|
||||
break;
|
||||
case Token::SHL:
|
||||
result = bvLeft << bvRight;
|
||||
break;
|
||||
case Token::SHR:
|
||||
solAssert(false, "");
|
||||
case Token::SAR:
|
||||
result = isSigned ?
|
||||
smtutil::Expression::ashr(bvLeft, bvRight) :
|
||||
bvLeft >> bvRight;
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
||||
solAssert(result, "");
|
||||
defineExpr(_op, smtutil::Expression::bv2int(*result, isSigned));
|
||||
defineExpr(_op, bitwiseOperation(
|
||||
_op.getOperator(),
|
||||
expr(_op.leftExpression(), commonType),
|
||||
expr(_op.rightExpression(), commonType),
|
||||
commonType
|
||||
));
|
||||
}
|
||||
|
||||
void SMTEncoder::bitwiseNotOperation(UnaryOperation const& _op)
|
||||
@@ -1604,9 +1609,27 @@ smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment
|
||||
{Token::AssignDiv, Token::Div},
|
||||
{Token::AssignMod, Token::Mod}
|
||||
};
|
||||
static map<Token, Token> const compoundToBitwise{
|
||||
{Token::AssignBitAnd, Token::BitAnd},
|
||||
{Token::AssignBitOr, Token::BitOr},
|
||||
{Token::AssignBitXor, Token::BitXor},
|
||||
{Token::AssignShl, Token::SHL},
|
||||
{Token::AssignShr, Token::SHR},
|
||||
{Token::AssignSar, Token::SAR}
|
||||
};
|
||||
Token op = _assignment.assignmentOperator();
|
||||
solAssert(compoundToArithmetic.count(op), "");
|
||||
solAssert(compoundToArithmetic.count(op) || compoundToBitwise.count(op), "");
|
||||
|
||||
auto decl = identifierToVariable(_assignment.leftHandSide());
|
||||
|
||||
if (compoundToBitwise.count(op))
|
||||
return bitwiseOperation(
|
||||
compoundToBitwise.at(op),
|
||||
decl ? currentValue(*decl) : expr(_assignment.leftHandSide()),
|
||||
expr(_assignment.rightHandSide()),
|
||||
_assignment.annotation().type
|
||||
);
|
||||
|
||||
auto values = arithmeticOperation(
|
||||
compoundToArithmetic.at(op),
|
||||
decl ? currentValue(*decl) : expr(_assignment.leftHandSide()),
|
||||
|
||||
@@ -119,6 +119,14 @@ protected:
|
||||
TypePointer const& _commonType,
|
||||
Expression const& _expression
|
||||
);
|
||||
|
||||
smtutil::Expression bitwiseOperation(
|
||||
Token _op,
|
||||
smtutil::Expression const& _left,
|
||||
smtutil::Expression const& _right,
|
||||
TypePointer const& _commonType
|
||||
);
|
||||
|
||||
void compareOperation(BinaryOperation const& _op);
|
||||
void booleanOperation(BinaryOperation const& _op);
|
||||
void bitwiseOperation(BinaryOperation const& _op);
|
||||
|
||||
Reference in New Issue
Block a user