Merge pull request #10605 from ethereum/develop

Merge develop into breaking.
This commit is contained in:
chriseth 2020-12-15 14:01:01 +01:00 committed by GitHub
commit ffaf40950a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 361 additions and 46 deletions

View File

@ -47,6 +47,7 @@ AST Changes:
Language Features:
* Code generator: Support copying dynamically encoded structs from calldata to memory.
* Code generator: Support copying of nested arrays from calldata to memory.
* Code generator: Support conversion from calldata slices to memory and storage arrays.
* The fallback function can now also have a single ``calldata`` argument (equaling ``msg.data``) and return ``bytes memory`` (which will not be ABI-encoded but returned as-is).
* Wasm backend: Add ``i32.select`` and ``i64.select`` instructions.
@ -69,6 +70,7 @@ Bugfixes:
* SMTChecker: Fix internal compiler error when doing bitwise compound assignment with string literals.
* SMTChecker: Fix internal error when trying to generate counterexamples with old z3.
* SMTChecker: Fix segmentation fault that could occur on certain SMT-enabled sources when no SMT solver was available.
* SMTChecker: Fix internal error when ``bytes.push()`` is used as the LHS of an assignment.
* Type Checker: ``super`` is not available in libraries.
* Type Checker: Disallow leading zeroes in sized-types (e.g. ``bytes000032``), but allow them to be treated as identifiers.
* Yul Optimizer: Fix a bug in NameSimplifier where a new name created by NameSimplifier could also be created by NameDispenser.

View File

@ -1895,9 +1895,13 @@ std::unique_ptr<ReferenceType> ArrayType::copyForLocation(DataLocation _location
BoolResult ArraySliceType::isImplicitlyConvertibleTo(Type const& _other) const
{
if (m_arrayType.location() == DataLocation::CallData && m_arrayType.isDynamicallySized() && m_arrayType == _other)
return true;
return (*this) == _other;
return
(*this) == _other ||
(
m_arrayType.dataStoredIn(DataLocation::CallData) &&
m_arrayType.isDynamicallySized() &&
m_arrayType.isImplicitlyConvertibleTo(_other)
);
}
string ArraySliceType::richIdentifier() const

View File

@ -1050,18 +1050,18 @@ void CompilerUtils::convertType(
}
case Type::Category::ArraySlice:
{
solAssert(_targetType.category() == Type::Category::Array, "");
auto& typeOnStack = dynamic_cast<ArraySliceType const&>(_typeOnStack);
solUnimplementedAssert(
_targetType.dataStoredIn(DataLocation::CallData),
"Conversion from calldata slices to memory not yet implemented."
);
solAssert(_targetType == typeOnStack.arrayType(), "");
solUnimplementedAssert(
typeOnStack.arrayType().location() == DataLocation::CallData &&
auto const& targetArrayType = dynamic_cast<ArrayType const&>(_targetType);
solAssert(typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType), "");
solAssert(
typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) &&
typeOnStack.arrayType().isDynamicallySized() &&
!typeOnStack.arrayType().baseType()->isDynamicallyEncoded(),
""
);
if (!_targetType.dataStoredIn(DataLocation::CallData))
return convertType(typeOnStack.arrayType(), _targetType);
break;
}
case Type::Category::Struct:

View File

@ -2946,19 +2946,21 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
}
else if (_from.category() == Type::Category::ArraySlice)
{
solAssert(_from.isDynamicallySized(), "");
solAssert(_from.dataStoredIn(DataLocation::CallData), "");
solAssert(_to.category() == Type::Category::Array, "");
auto const& fromType = dynamic_cast<ArraySliceType const&>(_from);
auto const& targetType = dynamic_cast<ArrayType const&>(_to);
ArraySliceType const& fromType = dynamic_cast<ArraySliceType const&>(_from);
ArrayType const& targetType = dynamic_cast<ArrayType const&>(_to);
solAssert(!fromType.arrayType().baseType()->isDynamicallyEncoded(), "");
solAssert(fromType.arrayType().isImplicitlyConvertibleTo(targetType), "");
solAssert(
*fromType.arrayType().baseType() == *targetType.baseType(),
"Converting arrays of different type is not possible"
fromType.arrayType().dataStoredIn(DataLocation::CallData) &&
fromType.arrayType().isDynamicallySized() &&
!fromType.arrayType().baseType()->isDynamicallyEncoded(),
""
);
if (!targetType.dataStoredIn(DataLocation::CallData))
return arrayConversionFunction(fromType.arrayType(), targetType);
string const functionName =
"convert_" +
_from.identifier() +

View File

@ -507,18 +507,21 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
{
solAssert(smt::isInteger(*type) || smt::isFixedPoint(*type), "");
solAssert(subExpr->annotation().willBeWrittenTo, "");
auto computeNewValue = [&](auto currentValue) {
return arithmeticOperation(
_op.getOperator() == Token::Inc ? Token::Add : Token::Sub,
currentValue,
smtutil::Expression(size_t(1)),
_op.annotation().type,
_op
).first;
};
if (auto identifier = dynamic_cast<Identifier const*>(subExpr))
{
auto decl = identifierToVariable(*identifier);
solAssert(decl, "");
auto innerValue = currentValue(*decl);
auto newValue = arithmeticOperation(
_op.getOperator() == Token::Inc ? Token::Add : Token::Sub,
innerValue,
smtutil::Expression(size_t(1)),
_op.annotation().type,
_op
).first;
auto newValue = computeNewValue(innerValue);
defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue);
assignment(*decl, newValue);
}
@ -528,16 +531,17 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
)
{
auto innerValue = expr(*subExpr);
auto newValue = arithmeticOperation(
_op.getOperator() == Token::Inc ? Token::Add : Token::Sub,
innerValue,
smtutil::Expression(size_t(1)),
_op.annotation().type,
_op
).first;
auto newValue = computeNewValue(innerValue);
defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue);
indexOrMemberAssignment(*subExpr, newValue);
}
else if (isEmptyPush(*subExpr))
{
auto innerValue = expr(*subExpr);
auto newValue = computeNewValue(innerValue);
defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue);
arrayPushPopAssign(*subExpr, newValue);
}
else
solAssert(false, "");
@ -709,6 +713,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
break;
}
case FunctionType::Kind::ArrayPush:
case FunctionType::Kind::ByteArrayPush:
arrayPush(_funCall);
break;
case FunctionType::Kind::ArrayPop:
@ -1134,8 +1139,12 @@ void SMTEncoder::visitFunctionIdentifier(Identifier const& _identifier)
void SMTEncoder::visitStructConstructorCall(FunctionCall const& _funCall)
{
solAssert(*_funCall.annotation().kind == FunctionCallKind::StructConstructorCall, "");
auto& structSymbolicVar = dynamic_cast<smt::SymbolicStructVariable&>(*m_context.expression(_funCall));
structSymbolicVar.assignAllMembers(applyMap(_funCall.sortedArguments(), [this](auto const& arg) { return expr(*arg); }));
if (smt::isNonRecursiveStruct(*_funCall.annotation().type))
{
auto& structSymbolicVar = dynamic_cast<smt::SymbolicStructVariable&>(*m_context.expression(_funCall));
structSymbolicVar.assignAllMembers(applyMap(_funCall.sortedArguments(), [this](auto const& arg) { return expr(*arg); }));
}
}
void SMTEncoder::endVisit(Literal const& _literal)
@ -1567,6 +1576,8 @@ void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression
else if (auto const* funCall = dynamic_cast<FunctionCall const*>(expr))
{
FunctionType const& funType = dynamic_cast<FunctionType const&>(*funCall->expression().annotation().type);
// Push cannot occur on an expression that is itself a ByteArrayPush, i.e., bytes.push().push() is not possible.
solAssert(funType.kind() != FunctionType::Kind::ByteArrayPush, "");
if (funType.kind() == FunctionType::Kind::ArrayPush)
{
auto memberAccess = dynamic_cast<MemberAccess const*>(&funCall->expression());
@ -2494,7 +2505,7 @@ MemberAccess const* SMTEncoder::isEmptyPush(Expression const& _expr) const
)
{
auto const& funType = dynamic_cast<FunctionType const&>(*funCall->expression().annotation().type);
if (funType.kind() == FunctionType::Kind::ArrayPush)
if (funType.kind() == FunctionType::Kind::ArrayPush || funType.kind() == FunctionType::Kind::ByteArrayPush)
return &dynamic_cast<MemberAccess const&>(funCall->expression());
}
return nullptr;
@ -2542,10 +2553,10 @@ FunctionDefinition const* SMTEncoder::functionCallToDefinition(FunctionCall cons
FunctionDefinition const* funDef = nullptr;
Expression const* calledExpr = &_funCall.expression();
if (TupleExpression const* fun = dynamic_cast<TupleExpression const*>(&_funCall.expression()))
if (TupleExpression const* fun = dynamic_cast<TupleExpression const*>(calledExpr))
{
solAssert(fun->components().size() == 1, "");
calledExpr = fun->components().front().get();
calledExpr = innermostTuple(*calledExpr);
}
if (Identifier const* fun = dynamic_cast<Identifier const*>(calledExpr))
@ -2596,8 +2607,11 @@ vector<VariableDeclaration const*> SMTEncoder::modifiersVariables(FunctionDefini
continue;
visited.insert(modifier);
vars += applyMap(modifier->parameters(), [](auto _var) { return _var.get(); });
vars += BlockVars(modifier->body()).vars;
if (modifier->isImplemented())
{
vars += applyMap(modifier->parameters(), [](auto _var) { return _var.get(); });
vars += BlockVars(modifier->body()).vars;
}
}
return vars;
}
@ -2684,6 +2698,7 @@ vector<smtutil::Expression> SMTEncoder::symbolicArguments(FunctionCall const& _f
unsigned firstParam = 0;
if (funType->bound())
{
calledExpr = innermostTuple(*calledExpr);
auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr);
solAssert(boundFunction, "");
args.push_back(expr(boundFunction->expression(), functionParams.front()->type()));

View File

@ -0,0 +1,19 @@
pragma abicoder v2;
contract Test {
function f(uint256[] calldata c) internal returns (uint a, uint b) {
return (c.length, c[0]);
}
function g(uint256[] calldata c) external returns (uint a, uint b) {
return f(c);
}
function h(uint256[] calldata c, uint start, uint end) external returns (uint a, uint b) {
return f(c[start: end]);
}
}
// ====
// compileViaYul: also
// ----
// g(uint256[]): 0x20, 4, 1, 2, 3, 4 -> 4, 1
// h(uint256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 2, 2

View File

@ -0,0 +1,39 @@
contract C {
function f1(bytes calldata c1, uint256 s, uint256 e, bytes calldata c2) public returns (bool) {
return keccak256(c1[s:e]) == keccak256(c2);
}
function f2(bytes calldata c, uint256 s) public returns (uint256, bytes memory) {
return abi.decode(c[s:], (uint256, bytes));
}
function f3(bytes calldata c1, uint256 s, uint256 e, bytes calldata c2) public returns (bool) {
bytes memory a = abi.encode(c1[s:e]);
bytes memory b = abi.encode(c2);
if (a.length != b.length) { return false; }
for (uint256 i = 0; i < a.length; i++) {
if (a[i] != b[i]) { return false; }
}
return true;
}
function f4(bytes calldata c1, uint256 s, uint256 e, bytes calldata c2) public returns (bool) {
bytes memory a = abi.encodePacked(c1[s:e]);
bytes memory b = abi.encodePacked(c2);
if (a.length != b.length) { return false; }
for (uint256 i = 0; i < a.length; i++) {
if (a[i] != b[i]) { return false; }
}
return true;
}
}
// ====
// compileViaYul: also
// ----
// f1(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcde" -> true
// f1(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcdf" -> false
// f2(bytes,uint256): 0x40, 0, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg"
// f3(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcde" -> true
// f3(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcdf" -> false
// f4(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcde" -> true
// f4(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcdf" -> false

View File

@ -0,0 +1,27 @@
pragma abicoder v2;
contract C {
struct S {
uint128 p1;
uint256[3] a;
uint32 p2;
}
function f(S[] calldata c) internal returns (S[] memory) {
return c;
}
function g(S[] calldata c, uint256 s, uint256 e) public returns (S[] memory) {
return f(c[s:e]);
}
function f1(uint256[3][] calldata c) internal returns (uint256[3][] memory) {
return c;
}
function g1(uint256[3][] calldata c, uint256 s, uint256 e) public returns (uint256[3][] memory) {
return f1(c[s:e]);
}
}
// ====
// compileViaYul: also
// ----
// g((uint128, uint256[3], uint32)[], uint256, uint256): 0x60, 1, 3, 4, 55, 1, 2, 3, 66, 66, 2, 3, 4, 77, 77, 3, 4, 5, 88, 88, 4, 5, 6, 99 -> 0x20, 2, 66, 2, 3, 4, 77, 77, 3, 4, 5, 88
// g1(uint256[3][], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 -> 0x20, 2, 4, 5, 6, 7, 8, 9

View File

@ -0,0 +1,29 @@
contract C {
function f(int[] calldata b, uint256 start, uint256 end) public returns (int) {
int[] memory m = b[start:end];
uint len = end - start;
assert(len == m.length);
for (uint i = 0; i < len; i++) {
assert(b[start:end][i] == m[i]);
}
return [b[start:end]][0][0];
}
function g(int[] calldata b, uint256 start, uint256 end) public returns (int[] memory) {
return b[start:end];
}
function h1(int[] memory b) internal returns (int[] memory) {
return b;
}
function h(int[] calldata b, uint256 start, uint256 end) public returns (int[] memory) {
return h1(b[start:end]);
}
}
// ====
// compileViaYul: also
// ----
// f(int256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 2
// g(int256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 0x20, 2, 2, 3
// h(int256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 0x20, 2, 2, 3

View File

@ -0,0 +1,16 @@
contract C {
int[] s;
function f(int[] calldata b, uint256 start, uint256 end) public returns (int) {
s = b[start:end];
uint len = end - start;
assert(len == s.length);
for (uint i = 0; i < len; i++) {
assert(b[start:end][i] == s[i]);
}
return s[0];
}
}
// ====
// compileViaYul: also
// ----
// f(int256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 2

View File

@ -0,0 +1,16 @@
pragma experimental SMTChecker;
contract C {
bytes b;
function f() public {
b.push() = b.push();
uint length = b.length;
assert(length >= 2);
assert(b[length - 1] == 0);
assert(b[length - 1] == b[length - 2]);
// Fails
assert(b[length - 1] == byte(uint8(1)));
}
}
// ----
// Warning 6328: (236-275): CHC: Assertion violation happens here.\nCounterexample:\nb = [0, 0]\n\n\n\nTransaction trace:\nconstructor()\nState: b = []\nf()

View File

@ -0,0 +1,22 @@
pragma experimental SMTChecker;
contract C {
bytes b;
function f() public {
require(b.length == 0);
b.push() = byte(uint8(1));
assert(b[0] == byte(uint8(1)));
}
function g() public {
byte one = byte(uint8(1));
b.push() = one;
assert(b[b.length - 1] == one);
// Fails
assert(b[b.length - 1] == byte(uint8(100)));
}
}
// ----
// Warning 6328: (290-333): CHC: Assertion violation happens here.\nCounterexample:\nb = [1]\n\n\n\nTransaction trace:\nconstructor()\nState: b = []\ng()

View File

@ -0,0 +1,26 @@
pragma experimental SMTChecker;
contract C {
bytes[] c;
function f() public {
bytes1 val = bytes1(uint8(2));
require(c.length == 0);
c.push().push() = val;
assert(c.length == 1);
assert(c[0].length == 1);
assert(c[0][0] == val);
}
function g() public {
bytes1 val = bytes1(uint8(2));
c.push().push() = val;
assert(c.length > 0);
assert(c[c.length - 1].length == 1);
assert(c[c.length - 1][c[c.length - 1].length - 1] == val);
// Fails
assert(c[c.length - 1][c[c.length - 1].length - 1] == bytes1(uint8(100)));
}
}
// ----
// Warning 6328: (468-541): CHC: Assertion violation happens here.\nCounterexample:\nc = [[2]]\n\n\n\nTransaction trace:\nconstructor()\nState: c = []\ng()

View File

@ -0,0 +1,15 @@
pragma experimental SMTChecker;
contract C {
uint x;
function f() public {
x = 0;
((inc))();
assert(x == 1); // should hold
}
function inc() internal returns (uint) {
require(x < 100);
return ++x;
}
}

View File

@ -0,0 +1,23 @@
pragma experimental SMTChecker;
library L {
struct S {
uint256[] data;
}
function f(S memory _s) internal pure returns (uint256) {
require(_s.data.length > 0);
return 42;
}
}
contract C {
using L for L.S;
function f() public pure returns (uint256 y) {
L.S memory x;
y = (x.f)();
assert(y == 42); // should hold
}
}
// ----
// Warning 6031: (289-292): Internal error: Expression undefined for SMT solver.
// Warning 6031: (289-292): Internal error: Expression undefined for SMT solver.

View File

@ -0,0 +1,6 @@
pragma experimental SMTChecker;
abstract contract A {
function f() public mod {}
modifier mod virtual;
}

View File

@ -0,0 +1,13 @@
pragma experimental SMTChecker;
contract C {
uint[] x;
function f() public {
require(x.length == 0);
++x.push();
assert(x.length == 1);
assert(x[0] == 1); // should hold
assert(x[0] == 42); // should fail
}
}
// ----
// Warning 6328: (182-200): CHC: Assertion violation happens here.\nCounterexample:\nx = [1]\n\n\n\nTransaction trace:\nconstructor()\nState: x = []\nf()

View File

@ -0,0 +1,11 @@
pragma experimental SMTChecker;
contract C {
struct S {
int[][] d;
}
S[] data;
function f() public {
++data[1].d[3].push();
}
}

View File

@ -0,0 +1,13 @@
pragma experimental SMTChecker;
contract Test {
struct RecursiveStruct {
RecursiveStruct[] vals;
}
function func() public pure {
RecursiveStruct[1] memory val = [ RecursiveStruct(new RecursiveStruct[](42)) ];
}
}
// ----
// Warning 2072: (136-165): Unused local variable.
// Warning 8364: (170-212): Assertion checker does not yet implement type struct Test.RecursiveStruct memory
// Warning 8364: (170-212): Assertion checker does not yet implement type struct Test.RecursiveStruct memory

View File

@ -0,0 +1,21 @@
pragma experimental SMTChecker;
contract Test {
struct RecursiveStruct {
uint x;
RecursiveStruct[] vals;
}
function func() public pure {
RecursiveStruct memory val = RecursiveStruct(1, new RecursiveStruct[](42));
assert(val.x == 1);
}
}
// ----
// Warning 8115: (146-172): Assertion checker does not yet support the type of this variable.
// Warning 8364: (175-220): Assertion checker does not yet implement type struct Test.RecursiveStruct memory
// Warning 7650: (231-236): Assertion checker does not yet support this expression.
// Warning 8364: (231-234): Assertion checker does not yet implement type struct Test.RecursiveStruct memory
// Warning 6328: (224-242): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nfunc()
// Warning 8115: (146-172): Assertion checker does not yet support the type of this variable.
// Warning 8364: (175-220): Assertion checker does not yet implement type struct Test.RecursiveStruct memory
// Warning 7650: (231-236): Assertion checker does not yet support this expression.
// Warning 8364: (231-234): Assertion checker does not yet implement type struct Test.RecursiveStruct memory

View File

@ -5,4 +5,3 @@ contract c {
}
}
// ----
// TypeError 7407: (63-74): Type bytes calldata slice is not implicitly convertible to expected type bytes storage ref.

View File

@ -1,7 +1,5 @@
contract C {
function f(bytes calldata x) external {
bytes memory y = x[1:2];
function f(bytes calldata x) external pure returns (bytes memory) {
return x[1:2];
}
}
// ----
// TypeError 9574: (65-88): Type bytes calldata slice is not implicitly convertible to expected type bytes memory.
}

View File

@ -4,4 +4,3 @@ contract C {
}
}
// ----
// TypeError 9553: (79-85): Invalid type for argument in function call. Invalid implicit conversion from bytes calldata slice to bytes memory requested.