mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -89,8 +89,8 @@ For most of the topics the compiler will provide suggestions.
|
||||
|
||||
* Explicit data location for all variables of struct, array or mapping types is
|
||||
now mandatory. This is also applied to function parameters and return
|
||||
variables. For example, change ``uint[] x = m_x`` to ``uint[] storage x =
|
||||
m_x``, and ``function f(uint[][] x)`` to ``function f(uint[][] memory x)``
|
||||
variables. For example, change ``uint[] x = z`` to ``uint[] storage x =
|
||||
z``, and ``function f(uint[][] x)`` to ``function f(uint[][] memory x)``
|
||||
where ``memory`` is the data location and might be replaced by ``storage`` or
|
||||
``calldata`` accordingly. Note that ``external`` functions require
|
||||
parameters with a data location of ``calldata``.
|
||||
@@ -483,7 +483,7 @@ New version:
|
||||
return data;
|
||||
}
|
||||
|
||||
using address_make_payable for address;
|
||||
using AddressMakePayable for address;
|
||||
// Data location for 'arr' must be specified
|
||||
function g(uint[] memory /* arr */, bytes8 x, OtherContract otherContract, address unknownContract) public payable {
|
||||
// 'otherContract.transfer' is not provided.
|
||||
@@ -500,7 +500,7 @@ New version:
|
||||
// 'address payable' should be used whenever possible.
|
||||
// To increase clarity, we suggest the use of a library for
|
||||
// the conversion (provided after the contract in this example).
|
||||
address payable addr = unknownContract.make_payable();
|
||||
address payable addr = unknownContract.makePayable();
|
||||
require(addr.send(1 ether));
|
||||
|
||||
// Since uint32 (4 bytes) is smaller than bytes8 (8 bytes),
|
||||
@@ -516,8 +516,8 @@ New version:
|
||||
|
||||
// We can define a library for explicitly converting ``address``
|
||||
// to ``address payable`` as a workaround.
|
||||
library address_make_payable {
|
||||
function make_payable(address x) internal pure returns (address payable) {
|
||||
library AddressMakePayable {
|
||||
function makePayable(address x) internal pure returns (address payable) {
|
||||
return address(uint160(x));
|
||||
}
|
||||
}
|
||||
|
||||
+113
-7
@@ -45,19 +45,19 @@ Solidity language without a compiler change.
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
|
||||
library GetCode {
|
||||
function at(address _addr) public view returns (bytes memory o_code) {
|
||||
function at(address _addr) public view returns (bytes memory code) {
|
||||
assembly {
|
||||
// retrieve the size of the code, this needs assembly
|
||||
let size := extcodesize(_addr)
|
||||
// allocate output byte array - this could also be done without assembly
|
||||
// by using o_code = new bytes(size)
|
||||
o_code := mload(0x40)
|
||||
// by using code = new bytes(size)
|
||||
code := mload(0x40)
|
||||
// new "memory end" including padding
|
||||
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
|
||||
mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
|
||||
// store length in memory
|
||||
mstore(o_code, size)
|
||||
mstore(code, size)
|
||||
// actually retrieve the code, this needs assembly
|
||||
extcodecopy(_addr, add(o_code, 0x20), 0, size)
|
||||
extcodecopy(_addr, add(code, 0x20), 0, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ the variable will not point beyond ``calldatasize()`` is performed.
|
||||
For external function pointers the address and the function selector can be
|
||||
accessed using ``x.address`` and ``x.selector``.
|
||||
The selector consists of four right-aligned bytes.
|
||||
Both values are can be assigned to. For example:
|
||||
Both values can be assigned to. For example:
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
@@ -228,6 +228,11 @@ of their block is reached.
|
||||
Conventions in Solidity
|
||||
-----------------------
|
||||
|
||||
.. _assembly-typed-variables:
|
||||
|
||||
Values of Typed Variables
|
||||
=========================
|
||||
|
||||
In contrast to EVM assembly, Solidity has types which are narrower than 256 bits,
|
||||
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that
|
||||
types can be shorter than 256
|
||||
@@ -237,6 +242,11 @@ This means that if you access such a variable
|
||||
from within inline assembly, you might have to manually clean the higher-order bits
|
||||
first.
|
||||
|
||||
.. _assembly-memory-management:
|
||||
|
||||
Memory Management
|
||||
=================
|
||||
|
||||
Solidity manages memory in the following way. There is a "free memory pointer"
|
||||
at position ``0x40`` in memory. If you want to allocate memory, use the memory
|
||||
starting from where this pointer points at and update it.
|
||||
@@ -268,3 +278,99 @@ first slot of the array and followed by the array elements.
|
||||
Statically-sized memory arrays do not have a length field, but it might be added later
|
||||
to allow better convertibility between statically- and dynamically-sized arrays, so
|
||||
do not rely on this.
|
||||
|
||||
Memory Safety
|
||||
=============
|
||||
|
||||
Without the use of inline assembly, the compiler can rely on memory to remain in a well-defined
|
||||
state at all times. This is especially relevant for :ref:`the new code generation pipeline via Yul IR <ir-breaking-changes>`:
|
||||
this code generation path can move local variables from stack to memory to avoid stack-too-deep errors and
|
||||
perform additional memory optimizations, if it can rely on certain assumptions about memory use.
|
||||
|
||||
While we recommend to always respect Solidity's memory model, inline assembly allows you to use memory
|
||||
in an incompatible way. Therefore, moving stack variables to memory and additional memory optimizations are,
|
||||
by default, disabled in the presence of any inline assembly block that contains a memory operation or assigns
|
||||
to solidity variables in memory.
|
||||
|
||||
However, you can specifically annotate an assembly block to indicate that it in fact respects Solidity's memory
|
||||
model as follows:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
assembly ("memory-safe") {
|
||||
...
|
||||
}
|
||||
|
||||
In particular, a memory-safe assembly block may only access the following memory ranges:
|
||||
|
||||
- Memory allocated by yourself using a mechanism like the ``allocate`` function described above.
|
||||
- Memory allocated by Solidity, e.g. memory within the bounds of a memory array you reference.
|
||||
- The scratch space between memory offset 0 and 64 mentioned above.
|
||||
- Temporary memory that is located *after* the value of the free memory pointer at the beginning of the assembly block,
|
||||
i.e. memory that is "allocated" at the free memory pointer without updating the free memory pointer.
|
||||
|
||||
Furthermore, if the assembly block assigns to Solidity variables in memory, you need to assure that accesses to
|
||||
the Solidity variables only access these memory ranges.
|
||||
|
||||
Since this is mainly about the optimizer, these restrictions still need to be followed, even if the assembly block
|
||||
reverts or terminates. As an example, the following assembly snippet is not memory safe:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
assembly {
|
||||
returndatacopy(0, 0, returndatasize())
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
|
||||
But the following is:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
assembly ("memory-safe") {
|
||||
let p := mload(0x40)
|
||||
returndatacopy(p, 0, returndatasize())
|
||||
revert(p, returndatasize())
|
||||
}
|
||||
|
||||
Note that you do not need to update the free memory pointer if there is no following allocation,
|
||||
but you can only use memory starting from the current offset given by the free memory pointer.
|
||||
|
||||
If the memory operations use a length of zero, it is also fine to just use any offset (not only if it falls into the scratch space):
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
assembly ("memory-safe") {
|
||||
revert(0, 0)
|
||||
}
|
||||
|
||||
Note that not only memory operations in inline assembly itself can be memory-unsafe, but also assignments to
|
||||
solidity variables of reference type in memory. For example the following is not memory-safe:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
bytes memory x;
|
||||
assembly {
|
||||
x := 0x40
|
||||
}
|
||||
x[0x20] = 0x42;
|
||||
|
||||
Inline assembly that neither involves any operations that access memory nor assigns to any solidity variables
|
||||
in memory is automatically considered memory-safe and does not need to be annotated.
|
||||
|
||||
.. warning::
|
||||
It is your responsibility to make sure that the assembly actually satisfies the memory model. If you annotate
|
||||
an assembly block as memory-safe, but violate one of the memory assumptions, this **will** lead to incorrect and
|
||||
undefined behaviour that cannot easily be discovered by testing.
|
||||
|
||||
In case you are developing a library that is meant to be compatible across multiple versions
|
||||
of solidity, you can use a special comment to annotate an assembly block as memory-safe:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
...
|
||||
}
|
||||
|
||||
Note that we will disallow the annotation via comment in a future breaking release, so if you are not concerned with
|
||||
backwards-compatibility with older compiler versions, prefer using the dialect string.
|
||||
|
||||
@@ -102,10 +102,10 @@ two integers passed as function parameters, then you use something like:
|
||||
function arithmetic(uint _a, uint _b)
|
||||
public
|
||||
pure
|
||||
returns (uint o_sum, uint o_product)
|
||||
returns (uint sum, uint product)
|
||||
{
|
||||
o_sum = _a + _b;
|
||||
o_product = _a * _b;
|
||||
sum = _a + _b;
|
||||
product = _a * _b;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ statement:
|
||||
function arithmetic(uint _a, uint _b)
|
||||
public
|
||||
pure
|
||||
returns (uint o_sum, uint o_product)
|
||||
returns (uint sum, uint product)
|
||||
{
|
||||
return (_a + _b, _a * _b);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ It is also possible to extend elementary types in that way:
|
||||
}
|
||||
|
||||
Note that all external library calls are actual EVM function calls. This means that
|
||||
if you pass memory or value types, a copy will be performed, even of the
|
||||
if you pass memory or value types, a copy will be performed, even in case of the
|
||||
``self`` variable. The only situation where no copy will be performed
|
||||
is when storage reference variables are used or when internal library
|
||||
functions are called.
|
||||
|
||||
@@ -251,6 +251,12 @@ mode AssemblyBlockMode;
|
||||
AssemblyDialect: '"evmasm"';
|
||||
AssemblyLBrace: '{' -> popMode, pushMode(YulMode);
|
||||
|
||||
AssemblyFlagString: '"' DoubleQuotedStringCharacter+ '"';
|
||||
|
||||
AssemblyBlockLParen: '(';
|
||||
AssemblyBlockRParen: ')';
|
||||
AssemblyBlockComma: ',';
|
||||
|
||||
AssemblyBlockWS: [ \t\r\n\u000C]+ -> skip ;
|
||||
AssemblyBlockCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ;
|
||||
AssemblyBlockLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ;
|
||||
|
||||
@@ -476,7 +476,13 @@ revertStatement: Revert expression callArgumentList Semicolon;
|
||||
* The contents of an inline assembly block use a separate scanner/lexer, i.e. the set of keywords and
|
||||
* allowed identifiers is different inside an inline assembly block.
|
||||
*/
|
||||
assemblyStatement: Assembly AssemblyDialect? AssemblyLBrace yulStatement* YulRBrace;
|
||||
assemblyStatement: Assembly AssemblyDialect? assemblyFlags? AssemblyLBrace yulStatement* YulRBrace;
|
||||
|
||||
/**
|
||||
* Assembly flags.
|
||||
* Comma-separated list of double-quoted strings as flags.
|
||||
*/
|
||||
assemblyFlags: AssemblyBlockLParen AssemblyFlagString (AssemblyBlockComma AssemblyFlagString)* AssemblyBlockRParen;
|
||||
|
||||
//@doc:inline
|
||||
variableDeclarationList: variableDeclarations+=variableDeclaration (Comma variableDeclarations+=variableDeclaration)*;
|
||||
|
||||
@@ -10,7 +10,7 @@ A Simple Smart Contract
|
||||
|
||||
Let us begin with a basic example that sets the value of a variable and exposes
|
||||
it for other contracts to access. It is fine if you do not understand
|
||||
everything right now, we will go into more detail later.
|
||||
everything right now, we will go into more details later.
|
||||
|
||||
Storage Example
|
||||
===============
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
|
||||
.. index: ir breaking changes
|
||||
|
||||
.. _ir-breaking-changes:
|
||||
|
||||
*********************************
|
||||
Solidity IR-based Codegen Changes
|
||||
*********************************
|
||||
|
||||
@@ -204,7 +204,7 @@ Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
thisIsALongNestedMapping[being][set][to_some_value] = someFunction(
|
||||
thisIsALongNestedMapping[being][set][toSomeValue] = someFunction(
|
||||
argument1,
|
||||
argument2,
|
||||
argument3,
|
||||
@@ -215,7 +215,7 @@ No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
thisIsALongNestedMapping[being][set][to_some_value] = someFunction(argument1,
|
||||
thisIsALongNestedMapping[being][set][toSomeValue] = someFunction(argument1,
|
||||
argument2,
|
||||
argument3,
|
||||
argument4);
|
||||
@@ -439,15 +439,15 @@ Yes:
|
||||
|
||||
x = 1;
|
||||
y = 2;
|
||||
long_variable = 3;
|
||||
longVariable = 3;
|
||||
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
x = 1;
|
||||
y = 2;
|
||||
long_variable = 3;
|
||||
x = 1;
|
||||
y = 2;
|
||||
longVariable = 3;
|
||||
|
||||
Don't include a whitespace in the receive and fallback functions:
|
||||
|
||||
@@ -1092,12 +1092,10 @@ naming styles.
|
||||
* ``b`` (single lowercase letter)
|
||||
* ``B`` (single uppercase letter)
|
||||
* ``lowercase``
|
||||
* ``lower_case_with_underscores``
|
||||
* ``UPPERCASE``
|
||||
* ``UPPER_CASE_WITH_UNDERSCORES``
|
||||
* ``CapitalizedWords`` (or CapWords)
|
||||
* ``mixedCase`` (differs from CapitalizedWords by initial lowercase character!)
|
||||
* ``Capitalized_Words_With_Underscores``
|
||||
|
||||
.. note:: When using initialisms in CapWords, capitalize all the letters of the initialisms. Thus HTTPServerError is better than HttpServerError. When using initialisms in mixedCase, capitalize all the letters of the initialisms, except keep the first one lower case if it is the beginning of the name. Thus xmlHTTPRequest is better than XMLHTTPRequest.
|
||||
|
||||
@@ -1256,7 +1254,7 @@ Enums, in the style of simple type declarations, should be named using the CapWo
|
||||
Avoiding Naming Collisions
|
||||
==========================
|
||||
|
||||
* ``single_trailing_underscore_``
|
||||
* ``singleTrailingUnderscore_``
|
||||
|
||||
This convention is suggested when the desired name collides with that of a
|
||||
built-in or otherwise reserved name.
|
||||
|
||||
@@ -126,7 +126,7 @@ the ``sum`` function iterates over to sum all the values.
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.8 <0.9.0;
|
||||
pragma solidity ^0.8.8;
|
||||
|
||||
struct IndexValue { uint keyIndex; uint value; }
|
||||
struct KeyFlag { uint key; bool deleted; }
|
||||
@@ -137,6 +137,8 @@ the ``sum`` function iterates over to sum all the values.
|
||||
uint size;
|
||||
}
|
||||
|
||||
type Iterator is uint;
|
||||
|
||||
library IterableMapping {
|
||||
function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
|
||||
uint keyIndex = self.data[key].keyIndex;
|
||||
@@ -166,25 +168,29 @@ the ``sum`` function iterates over to sum all the values.
|
||||
return self.data[key].keyIndex > 0;
|
||||
}
|
||||
|
||||
function iterate_start(itmap storage self) internal view returns (uint keyIndex) {
|
||||
return iterate_next(self, type(uint).max);
|
||||
function iterateStart(itmap storage self) internal view returns (Iterator) {
|
||||
return iteratorSkipDeleted(self, 0);
|
||||
}
|
||||
|
||||
function iterate_valid(itmap storage self, uint keyIndex) internal view returns (bool) {
|
||||
return keyIndex < self.keys.length;
|
||||
function iterateValid(itmap storage self, Iterator iterator) internal view returns (bool) {
|
||||
return Iterator.unwrap(iterator) < self.keys.length;
|
||||
}
|
||||
|
||||
function iterate_next(itmap storage self, uint keyIndex) internal view returns (uint r_keyIndex) {
|
||||
keyIndex++;
|
||||
while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
|
||||
keyIndex++;
|
||||
return keyIndex;
|
||||
function iterateNext(itmap storage self, Iterator iterator) internal view returns (Iterator) {
|
||||
return iteratorSkipDeleted(self, Iterator.unwrap(iterator) + 1);
|
||||
}
|
||||
|
||||
function iterate_get(itmap storage self, uint keyIndex) internal view returns (uint key, uint value) {
|
||||
function iterateGet(itmap storage self, Iterator iterator) internal view returns (uint key, uint value) {
|
||||
uint keyIndex = Iterator.unwrap(iterator);
|
||||
key = self.keys[keyIndex].key;
|
||||
value = self.data[key].value;
|
||||
}
|
||||
|
||||
function iteratorSkipDeleted(itmap storage self, uint keyIndex) private view returns (Iterator) {
|
||||
while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
|
||||
keyIndex++;
|
||||
return Iterator.wrap(keyIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// How to use it
|
||||
@@ -206,11 +212,11 @@ the ``sum`` function iterates over to sum all the values.
|
||||
// Computes the sum of all stored data.
|
||||
function sum() public view returns (uint s) {
|
||||
for (
|
||||
uint i = data.iterate_start();
|
||||
data.iterate_valid(i);
|
||||
i = data.iterate_next(i)
|
||||
Iterator i = data.iterateStart();
|
||||
data.iterateValid(i);
|
||||
i = data.iterateNext(i)
|
||||
) {
|
||||
(, uint value) = data.iterate_get(i);
|
||||
(, uint value) = data.iterateGet(i);
|
||||
s += value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,11 +190,11 @@ If you want to use string parameters or other types that are not implicitly conv
|
||||
contract C {
|
||||
string s = "Storage";
|
||||
function f(bytes calldata bc, string memory sm, bytes16 b) public view {
|
||||
string memory concat_string = string.concat(s, string(bc), "Literal", sm);
|
||||
assert((bytes(s).length + bc.length + 7 + bytes(sm).length) == bytes(concat_string).length);
|
||||
string memory concatString = string.concat(s, string(bc), "Literal", sm);
|
||||
assert((bytes(s).length + bc.length + 7 + bytes(sm).length) == bytes(concatString).length);
|
||||
|
||||
bytes memory concat_bytes = bytes.concat(bytes(s), bc, bc[:2], "Literal", bytes(sm), b);
|
||||
assert((bytes(s).length + bc.length + 2 + 7 + bytes(sm).length + b.length) == concat_bytes.length);
|
||||
bytes memory concatBytes = bytes.concat(bytes(s), bc, bc[:2], "Literal", bytes(sm), b);
|
||||
assert((bytes(s).length + bc.length + 2 + 7 + bytes(sm).length + b.length) == concatBytes.length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,20 +376,20 @@ Array Members
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
|
||||
contract ArrayContract {
|
||||
uint[2**20] m_aLotOfIntegers;
|
||||
uint[2**20] aLotOfIntegers;
|
||||
// Note that the following is not a pair of dynamic arrays but a
|
||||
// dynamic array of pairs (i.e. of fixed size arrays of length two).
|
||||
// Because of that, T[] is always a dynamic array of T, even if T
|
||||
// itself is an array.
|
||||
// Data location for all state variables is storage.
|
||||
bool[2][] m_pairsOfFlags;
|
||||
bool[2][] pairsOfFlags;
|
||||
|
||||
// newPairs is stored in memory - the only possibility
|
||||
// for public contract function arguments
|
||||
function setAllFlagPairs(bool[2][] memory newPairs) public {
|
||||
// assignment to a storage array performs a copy of ``newPairs`` and
|
||||
// replaces the complete array ``m_pairsOfFlags``.
|
||||
m_pairsOfFlags = newPairs;
|
||||
// replaces the complete array ``pairsOfFlags``.
|
||||
pairsOfFlags = newPairs;
|
||||
}
|
||||
|
||||
struct StructType {
|
||||
@@ -411,45 +411,45 @@ Array Members
|
||||
|
||||
function setFlagPair(uint index, bool flagA, bool flagB) public {
|
||||
// access to a non-existing index will throw an exception
|
||||
m_pairsOfFlags[index][0] = flagA;
|
||||
m_pairsOfFlags[index][1] = flagB;
|
||||
pairsOfFlags[index][0] = flagA;
|
||||
pairsOfFlags[index][1] = flagB;
|
||||
}
|
||||
|
||||
function changeFlagArraySize(uint newSize) public {
|
||||
// using push and pop is the only way to change the
|
||||
// length of an array
|
||||
if (newSize < m_pairsOfFlags.length) {
|
||||
while (m_pairsOfFlags.length > newSize)
|
||||
m_pairsOfFlags.pop();
|
||||
} else if (newSize > m_pairsOfFlags.length) {
|
||||
while (m_pairsOfFlags.length < newSize)
|
||||
m_pairsOfFlags.push();
|
||||
if (newSize < pairsOfFlags.length) {
|
||||
while (pairsOfFlags.length > newSize)
|
||||
pairsOfFlags.pop();
|
||||
} else if (newSize > pairsOfFlags.length) {
|
||||
while (pairsOfFlags.length < newSize)
|
||||
pairsOfFlags.push();
|
||||
}
|
||||
}
|
||||
|
||||
function clear() public {
|
||||
// these clear the arrays completely
|
||||
delete m_pairsOfFlags;
|
||||
delete m_aLotOfIntegers;
|
||||
delete pairsOfFlags;
|
||||
delete aLotOfIntegers;
|
||||
// identical effect here
|
||||
m_pairsOfFlags = new bool[2][](0);
|
||||
pairsOfFlags = new bool[2][](0);
|
||||
}
|
||||
|
||||
bytes m_byteData;
|
||||
bytes byteData;
|
||||
|
||||
function byteArrays(bytes memory data) public {
|
||||
// byte arrays ("bytes") are different as they are stored without padding,
|
||||
// but can be treated identical to "uint8[]"
|
||||
m_byteData = data;
|
||||
byteData = data;
|
||||
for (uint i = 0; i < 7; i++)
|
||||
m_byteData.push();
|
||||
m_byteData[3] = 0x08;
|
||||
delete m_byteData[2];
|
||||
byteData.push();
|
||||
byteData[3] = 0x08;
|
||||
delete byteData[2];
|
||||
}
|
||||
|
||||
function addFlag(bool[2] memory flag) public returns (uint) {
|
||||
m_pairsOfFlags.push(flag);
|
||||
return m_pairsOfFlags.length;
|
||||
pairsOfFlags.push(flag);
|
||||
return pairsOfFlags.length;
|
||||
}
|
||||
|
||||
function createMemoryArray(uint size) public pure returns (bytes memory) {
|
||||
|
||||
@@ -409,12 +409,13 @@ Input Description
|
||||
"source1.sol": ["contract1"],
|
||||
"source2.sol": ["contract2", "contract3"]
|
||||
},
|
||||
// Choose whether division and modulo operations should be replaced by
|
||||
// multiplication with slack variables. Default is `true`.
|
||||
// Using `false` here is recommended if you are using the CHC engine
|
||||
// Choose how division and modulo operations should be encoded.
|
||||
// When using `false` they are replaced by multiplication with slack
|
||||
// variables. This is the default.
|
||||
// Using `true` here is recommended if you are using the CHC engine
|
||||
// and not using Spacer as the Horn solver (using Eldarica, for example).
|
||||
// See the Formal Verification section for a more detailed explanation of this option.
|
||||
"divModWithSlacks": true,
|
||||
"divModNoSlacks": false,
|
||||
// Choose which model checker engine to use: all (default), bmc, chc, none.
|
||||
"engine": "chc",
|
||||
// Choose which types of invariants should be reported to the user: contract, reentrancy.
|
||||
|
||||
+1
-1
@@ -768,7 +768,7 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| Instruction | | | Explanation |
|
||||
+=========================+=====+===+=================================================================+
|
||||
| stop() + `-` | F | stop execution, identical to return(0, 0) |
|
||||
| stop() | `-` | F | stop execution, identical to return(0, 0) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| add(x, y) | | F | x + y |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
|
||||
Reference in New Issue
Block a user