Merge pull request #9285 from ethereum/no_dot_in_asm

[BREAKING] Change _slot and _offset to use dot in inline assembly
This commit is contained in:
chriseth
2020-07-13 17:46:39 +02:00
committed by GitHub
64 changed files with 178 additions and 146 deletions
+1
View File
@@ -14,6 +14,7 @@ Breaking changes:
* Type checker: Disallow events with same name and parameter types in inheritance hierarchy.
* ``using A for B`` only affects the contract it is mentioned in and not all derived contracts
* Inline Assembly: Disallow `.` in user-defined function and variable names.
* Inline Assembly: Slot and offset of storage pointer variable ``x`` are accessed via ``x.slot`` and ``x.offset`` instead of ``x_slot`` and ``x_offset``.
Language Features:
* Yul: Disallow EVM instruction `pc()`.
+1
View File
@@ -21,3 +21,4 @@ This section gives detailed instructions on how to update prior code for every b
* Repeat the ``using A for B`` statements in all derived contracts if needed.
* Remove the ``public`` keyword from every constructor.
* Remove the ``internal`` keyword from every constructor and add ``abstract`` to the contract (if not already present).
* Change ``_slot`` and ``_offset`` suffixes in inline assembly to ``.slot`` and ``.offset``, respectively.
+5 -2
View File
@@ -306,7 +306,7 @@ assemblyBlock
: '{' assemblyItem* '}' ;
assemblyExpression
: assemblyCall | assemblyLiteral ;
: assemblyCall | assemblyLiteral | assemblyIdentifier ;
assemblyCall
: ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ;
@@ -318,7 +318,10 @@ assemblyAssignment
: assemblyIdentifierList ':=' assemblyExpression ;
assemblyIdentifierList
: identifier ( ',' identifier )* ;
: assemblyIdentifier ( ',' assemblyIdentifier )* ;
assemblyIdentifier
: identifier ( '.' identifier )* ;
assemblyStackAssignment
: '=:' identifier ;
+13 -11
View File
@@ -132,14 +132,15 @@ For local storage variables or state variables, a single Yul identifier
is not sufficient, since they do not necessarily occupy a single full storage slot.
Therefore, their "address" is composed of a slot and a byte-offset
inside that slot. To retrieve the slot pointed to by the variable ``x``, you
use ``x_slot``, and to retrieve the byte-offset you use ``x_offset``.
use ``x.slot``, and to retrieve the byte-offset you use ``x.offset``.
Using ``x`` itself will result in an error.
Local Solidity variables are available for assignments, for example:
.. code::
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.8.0;
pragma solidity >0.6.99 <0.8.0;
contract C {
uint b;
@@ -147,7 +148,7 @@ Local Solidity variables are available for assignments, for example:
assembly {
// We ignore the storage slot offset, we know it is zero
// in this special case.
r := mul(x, sload(b_slot))
r := mul(x, sload(b.slot))
}
}
}
@@ -164,20 +165,21 @@ Local Solidity variables are available for assignments, for example:
``assembly { signextend(<num_bytes_of_x_minus_one>, x) }``
Since Solidity 0.6.0 the name of a inline assembly variable may not end in ``_offset`` or ``_slot``
and it may not shadow any declaration visible in the scope of the inline assembly block
(including variable, contract and function declarations). Similarly, if the name of a declared
variable contains a dot ``.``, the prefix up to the ``.`` may not conflict with any
declaration visible in the scope of the inline assembly block.
Since Solidity 0.6.0 the name of a inline assembly variable may not
shadow any declaration visible in the scope of the inline assembly block
(including variable, contract and function declarations).
Since Solidity 0.7.0, variables and functions declared inside the
inline assembly block may not contain ``.``, but using ``.`` is
valid to access Solidity variables from outside the inline assembly block.
Assignments are possible to assembly-local variables and to function-local
variables. Take care that when you assign to variables that point to
memory or storage, you will only change the pointer and not the data.
You can assign to the ``_slot`` part of a local storage variable pointer.
For these (structs, arrays or mappings), the ``_offset`` part is always zero.
It is not possible to assign to the ``_slot`` or ``_offset`` part of a state variable,
You can assign to the ``.slot`` part of a local storage variable pointer.
For these (structs, arrays or mappings), the ``.offset`` part is always zero.
It is not possible to assign to the ``.slot`` or ``.offset`` part of a state variable,
though.
+23 -18
View File
@@ -33,6 +33,7 @@
#include <liblangutil/Exceptions.h>
#include <libsolutil/StringUtils.h>
#include <libsolutil/CommonData.h>
#include <boost/algorithm/string.hpp>
@@ -195,9 +196,10 @@ void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
void ReferencesResolver::operator()(yul::Identifier const& _identifier)
{
bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot");
bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset");
bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), ".slot");
bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), ".offset");
// Could also use `pathFromCurrentScope`, split by '.'
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
if (isSlot || isOffset)
{
@@ -207,19 +209,22 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
return;
string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - (
isSlot ?
string("_slot").size() :
string("_offset").size()
string(".slot").size() :
string(".offset").size()
));
if (realName.empty())
{
m_errorReporter.declarationError(
4794_error,
_identifier.location,
"In variable names _slot and _offset can only be used as a suffix."
"In variable names .slot and .offset can only be used as a suffix."
);
return;
}
declarations = m_resolver.nameFromCurrentScope(realName);
if (!declarations.empty())
// To support proper path resolution, we have to use pathFromCurrentScope.
solAssert(!util::contains(realName, '.'), "");
}
if (declarations.size() > 1)
{
@@ -231,7 +236,18 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
return;
}
else if (declarations.size() == 0)
{
if (
boost::algorithm::ends_with(_identifier.name.str(), "_slot") ||
boost::algorithm::ends_with(_identifier.name.str(), "_offset")
)
m_errorReporter.declarationError(
9467_error,
_identifier.location,
"Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables."
);
return;
}
if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front()))
if (var->isLocalVariable() && m_yulInsideFunction)
{
@@ -254,18 +270,9 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
{
validateYulIdentifierName(identifier.name, identifier.location);
bool isSlot = boost::algorithm::ends_with(identifier.name.str(), "_slot");
bool isOffset = boost::algorithm::ends_with(identifier.name.str(), "_offset");
string namePrefix = identifier.name.str().substr(0, identifier.name.str().find('.'));
if (isSlot || isOffset)
m_errorReporter.declarationError(
9155_error,
identifier.location,
"In variable declarations _slot and _offset can not be used as a suffix."
);
else if (
auto declarations = m_resolver.nameFromCurrentScope(namePrefix);
if (
auto declarations = m_resolver.nameFromCurrentScope(identifier.name.str());
!declarations.empty()
)
{
@@ -277,8 +284,6 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
3859_error,
identifier.location,
ssl,
namePrefix.size() < identifier.name.str().size() ?
"The prefix of this declaration conflicts with a declaration outside the inline assembly block." :
"This declaration shadows a declaration outside the inline assembly block."
);
}
+6 -6
View File
@@ -767,7 +767,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
}
else if (requiresStorage)
{
m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes _offset and _slot can only be used on non-constant storage variables.");
m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes .offset and .slot can only be used on non-constant storage variables.");
return false;
}
else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast<Literal const*>(var->value().get()))
@@ -795,7 +795,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
{
if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
{
m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables.");
return false;
}
else if (_context == yul::IdentifierContext::LValue)
@@ -807,7 +807,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
}
else if (identifierInfo.isOffset)
{
m_errorReporter.typeError(9739_error, _identifier.location, "Only _slot can be assigned to.");
m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to.");
return false;
}
else
@@ -816,12 +816,12 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
}
else if (!var->isConstant() && var->isStateVariable())
{
m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.");
m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the .slot and .offset suffixes.");
return false;
}
else if (var->type()->dataStoredIn(DataLocation::Storage))
{
m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the _slot or _offset suffix to access storage reference variables.");
m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the .slot or .offset suffix to access storage reference variables.");
return false;
}
else if (var->type()->sizeOnStack() != 1)
@@ -835,7 +835,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
}
else if (requiresStorage)
{
m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables.");
return false;
}
else if (_context == yul::IdentifierContext::LValue)
@@ -117,7 +117,7 @@
"src": "97:17:1",
"value":
{
"name": "s_offset",
"name": "s.offset",
"nodeType": "YulIdentifier",
"src": "106:8:1"
},
@@ -139,7 +139,7 @@
"arguments":
[
{
"name": "s_slot",
"name": "s.slot",
"nodeType": "YulIdentifier",
"src": "128:6:1"
},
@@ -2,7 +2,7 @@ contract C {
struct S { uint x; }
S s;
function e() pure public {
assembly { let x := s_offset let y := mul(s_slot, 2) }
assembly { let x := s.offset let y := mul(s.slot, 2) }
}
}
@@ -178,7 +178,7 @@
"valueSize": 1
}
],
"operations": "{\n let x := s_offset\n let y := mul(s_slot, 2)\n}"
"operations": "{\n let x := s.offset\n let y := mul(s.slot, 2)\n}"
},
"children": [],
"id": 8,
@@ -2,38 +2,38 @@ contract c {
bytes data;
function test_short() public returns (uint256 r) {
assembly {
sstore(data_slot, 0)
sstore(data.slot, 0)
}
for (uint8 i = 0; i < 15; i++) {
data.push(bytes1(i));
}
assembly {
r := sload(data_slot)
r := sload(data.slot)
}
}
function test_long() public returns (uint256 r) {
assembly {
sstore(data_slot, 0)
sstore(data.slot, 0)
}
for (uint8 i = 0; i < 33; i++) {
data.push(bytes1(i));
}
assembly {
r := sload(data_slot)
r := sload(data.slot)
}
}
function test_pop() public returns (uint256 r) {
assembly {
sstore(data_slot, 0)
sstore(data.slot, 0)
}
for (uint8 i = 0; i < 32; i++) {
data.push(bytes1(i));
}
data.pop();
assembly {
r := sload(data_slot)
r := sload(data.slot)
}
}
}
@@ -6,7 +6,7 @@ contract C {
data.push(123);
delete data;
assembly {
ret := sload(data_slot)
ret := sload(data.slot)
}
}
@@ -8,7 +8,7 @@ contract C {
}
x = m;
assembly {
r := sload(x_slot)
r := sload(x.slot)
}
}
}
@@ -7,9 +7,9 @@ contract C {
uint256 off1;
uint256 off2;
assembly {
sstore(z_slot, 7)
off1 := z_offset
off2 := y_offset
sstore(z.slot, 7)
off1 := z.offset
off2 := y.offset
}
assert(off1 == 0);
assert(off2 == 2);
@@ -8,8 +8,8 @@ contract C {
uint256 off2;
assembly {
function f() -> o1 {
sstore(z_slot, 7)
o1 := y_offset
sstore(z.slot, 7)
o1 := y.offset
}
off2 := f()
}
@@ -10,8 +10,8 @@ contract C {
Data storage x = a;
uint256 off;
assembly {
sstore(x_slot, 7)
off := x_offset
sstore(x.slot, 7)
off := x.offset
}
assert(off == 0);
return true;
@@ -13,7 +13,7 @@ contract C {
bytes32 slot = keccak256(abi.encode(uint(1), uint(0)));
assembly {
_data_slot := slot
_data.slot := slot
}
}
@@ -12,13 +12,13 @@ contract C {
}
function g() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
assembly { ys := y.slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
function h() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
assembly { ys := y.slot }
(bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
@@ -12,13 +12,13 @@ contract C {
}
function g() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
assembly { ys := y.slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
function h() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
assembly { ys := y.slot }
(bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
@@ -11,13 +11,13 @@ contract C {
}
function g() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
assembly { ys := y.slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
function h() public returns (bool, uint256) {
uint256 ys;
assembly { ys := y_slot }
assembly { ys := y.slot }
(bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, ys));
return (success, success ? abi.decode(data,(uint256)) : 0);
}
@@ -12,7 +12,7 @@ contract C {
}
function g() public returns (bool, bool, uint256) {
uint256 s_ptr;
assembly { s_ptr := s_slot }
assembly { s_ptr := s.slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.g.selector, s_ptr));
return (L.g.selector == bytes4(keccak256("g(uint256[] storage)")), success, abi.decode(data, (uint256)));
}
@@ -10,7 +10,7 @@ contract C {
function f() public returns (bool, bool, uint256) {
uint256 s_ptr;
assembly { s_ptr := s_slot }
assembly { s_ptr := s.slot }
(bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, s_ptr));
return (L.f.selector == bytes4(keccak256("f(L.S storage)")), success, abi.decode(data, (uint256)));
}
@@ -20,7 +20,7 @@ contract C {
for (uint i = 3; i < len; i++)
{
assembly {
mstore(0, storageArray_slot)
mstore(0, storageArray.slot)
let pos := add(keccak256(0, 0x20), i)
if iszero(eq(sload(pos), 0)) {
@@ -2,7 +2,7 @@ contract C {
bytes b;
function f() public returns (bool correct) {
assembly {
sstore(b_slot, or("deadbeef", 0x08))
sstore(b.slot, or("deadbeef", 0x08))
}
byte s = b[3];
uint r;
@@ -2,8 +2,8 @@ contract C {
bytes b;
function f() public returns (bool correct) {
assembly {
sstore(b_slot, 0x41)
mstore(0, b_slot)
sstore(b.slot, 0x41)
mstore(0, b.slot)
sstore(keccak256(0, 0x20), "deadbeefdeadbeefdeadbeefdeadbeef")
}
byte s = b[31];
@@ -3,7 +3,7 @@ contract C {
function f() public returns (bool correct) {
s.push();
assembly {
mstore(0, s_slot)
mstore(0, s.slot)
sstore(keccak256(0, 0x20), 257)
}
uint8 x = s[0];
@@ -2,7 +2,7 @@ contract C {
uint8[1] s;
function f() public returns (bool correct) {
assembly {
sstore(s_slot, 257)
sstore(s.slot, 257)
}
uint8 x = s[0];
uint r;
@@ -6,7 +6,7 @@ contract C {
function f() public returns (bool correct) {
s.m.push();
assembly {
mstore(0, s_slot)
mstore(0, s.slot)
sstore(keccak256(0, 0x20), 257)
}
uint8 x = s.m[0];
@@ -4,28 +4,28 @@ contract C {
function f() internal pure {
S storage c;
assembly {
for {} eq(0,0) { c_slot := s_slot } {}
for {} eq(0,0) { c.slot := s.slot } {}
}
c;
}
function g() internal pure {
S storage c;
assembly {
for {} eq(0,1) { c_slot := s_slot } {}
for {} eq(0,1) { c.slot := s.slot } {}
}
c;
}
function h() internal pure {
S storage c;
assembly {
for {} eq(0,0) {} { c_slot := s_slot }
for {} eq(0,0) {} { c.slot := s.slot }
}
c;
}
function i() internal pure {
S storage c;
assembly {
for {} eq(0,1) {} { c_slot := s_slot }
for {} eq(0,1) {} { c.slot := s.slot }
}
c;
}
@@ -4,14 +4,14 @@ contract C {
function f() internal pure {
S storage c;
assembly {
for { c_slot := s_slot } iszero(0) {} {}
for { c.slot := s.slot } iszero(0) {} {}
}
c;
}
function g() internal pure {
S storage c;
assembly {
for { c_slot := s_slot } iszero(1) {} {}
for { c.slot := s.slot } iszero(1) {} {}
}
c;
}
@@ -4,7 +4,7 @@ contract C {
function f(bool flag) internal pure {
S storage c;
assembly {
if flag { c_slot := s_slot }
if flag { c.slot := s.slot }
}
c;
}
@@ -7,7 +7,7 @@ contract C {
assembly {
function f() { return(0, 0) }
f()
c_slot := s_slot
c.slot := s.slot
}
c;
}
@@ -4,7 +4,7 @@ contract C {
function f() internal pure {
S storage c;
assembly {
c_slot := s_slot
c.slot := s.slot
}
c;
}
@@ -5,7 +5,7 @@ contract C {
S storage c;
assembly {
switch a
case 0 { c_slot := s_slot }
case 0 { c.slot := s.slot }
}
c;
}
@@ -13,8 +13,8 @@ contract C {
S storage c;
assembly {
switch flag
case 0 { c_slot := s_slot }
case 1 { c_slot := s_slot }
case 0 { c.slot := s.slot }
case 1 { c.slot := s.slot }
}
c;
}
@@ -22,7 +22,7 @@ contract C {
S storage c;
assembly {
switch a
case 0 { c_slot := s_slot }
case 0 { c.slot := s.slot }
default { return(0,0) }
}
c;
@@ -5,8 +5,8 @@ contract C {
S storage c;
assembly {
switch flag
case 0 { c_slot := s_slot }
default { c_slot := s_slot }
case 0 { c.slot := s.slot }
default { c.slot := s.slot }
}
c;
}
@@ -15,7 +15,7 @@ contract C {
assembly {
switch a
case 0 { revert(0, 0) }
default { c_slot := s_slot }
default { c.slot := s.slot }
}
c;
}
@@ -3,22 +3,22 @@ contract C {
S s;
function f() internal pure returns (S storage c) {
assembly {
for {} eq(0,0) { c_slot := s_slot } {}
for {} eq(0,0) { c.slot := s.slot } {}
}
}
function g() internal pure returns (S storage c) {
assembly {
for {} eq(0,1) { c_slot := s_slot } {}
for {} eq(0,1) { c.slot := s.slot } {}
}
}
function h() internal pure returns (S storage c) {
assembly {
for {} eq(0,0) {} { c_slot := s_slot }
for {} eq(0,0) {} { c.slot := s.slot }
}
}
function i() internal pure returns (S storage c) {
assembly {
for {} eq(0,1) {} { c_slot := s_slot }
for {} eq(0,1) {} { c.slot := s.slot }
}
}
}
@@ -3,12 +3,12 @@ contract C {
S s;
function f() internal pure returns (S storage c) {
assembly {
for { c_slot := s_slot } iszero(0) {} {}
for { c.slot := s.slot } iszero(0) {} {}
}
}
function g() internal pure returns (S storage c) {
assembly {
for { c_slot := s_slot } iszero(1) {} {}
for { c.slot := s.slot } iszero(1) {} {}
}
}
}
@@ -3,7 +3,7 @@ contract C {
S s;
function f(bool flag) internal pure returns (S storage c) {
assembly {
if flag { c_slot := s_slot }
if flag { c.slot := s.slot }
}
}
}
@@ -6,7 +6,7 @@ contract C {
assembly {
function f() { return(0, 0) }
f()
c_slot := s_slot
c.slot := s.slot
}
}
}
@@ -3,7 +3,7 @@ contract C {
S s;
function f() internal pure returns (S storage c) {
assembly {
c_slot := s_slot
c.slot := s.slot
}
}
}
@@ -4,20 +4,20 @@ contract C {
function f(uint256 a) internal pure returns (S storage c) {
assembly {
switch a
case 0 { c_slot := s_slot }
case 0 { c.slot := s.slot }
}
}
function g(bool flag) internal pure returns (S storage c) {
assembly {
switch flag
case 0 { c_slot := s_slot }
case 1 { c_slot := s_slot }
case 0 { c.slot := s.slot }
case 1 { c.slot := s.slot }
}
}
function h(uint256 a) internal pure returns (S storage c) {
assembly {
switch a
case 0 { c_slot := s_slot }
case 0 { c.slot := s.slot }
default { return(0,0) }
}
}
@@ -4,15 +4,15 @@ contract C {
function f(bool flag) internal pure returns (S storage c) {
assembly {
switch flag
case 0 { c_slot := s_slot }
default { c_slot := s_slot }
case 0 { c.slot := s.slot }
default { c.slot := s.slot }
}
}
function g(uint256 a) internal pure returns (S storage c) {
assembly {
switch a
case 0 { revert(0, 0) }
default { c_slot := s_slot }
default { c.slot := s.slot }
}
}
}
@@ -4,7 +4,7 @@ contract C {
function f(uint256 a) internal pure returns (S storage c) {
assembly {
switch a
default { c_slot := s_slot }
default { c.slot := s.slot }
}
}
}
@@ -1,7 +1,7 @@
contract C {
uint[] r;
function f() internal view returns (uint[] storage s) {
assembly { pop(s_slot) }
assembly { pop(s.slot) }
s = r;
}
}
@@ -2,9 +2,9 @@ contract test {
uint constant x = 2;
function f() pure public {
assembly {
let r := x_offset
let r := x.offset
}
}
}
// ----
// TypeError 6617: (112-120): The suffixes _offset and _slot can only be used on non-constant storage variables.
// TypeError 6617: (112-120): The suffixes .offset and .slot can only be used on non-constant storage variables.
@@ -3,7 +3,7 @@ contract test {
function f() public {
uint[] storage a = r;
assembly {
function g() -> x { x := a_offset }
function g() -> x { x := a.offset }
}
}
}
@@ -7,4 +7,4 @@ contract test {
}
}
// ----
// TypeError 1408: (89-90): Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.
// TypeError 1408: (89-90): Only local variables are supported. To access storage variables, use the .slot and .offset suffixes.
@@ -10,4 +10,4 @@ contract test {
}
}
// ----
// TypeError 1408: (80-81): Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.
// TypeError 1408: (80-81): Only local variables are supported. To access storage variables, use the .slot and .offset suffixes.
@@ -2,7 +2,7 @@ contract test {
uint a;
function f() pure public {
assembly {
function g() -> x { x := a_slot }
function g() -> x { x := a.slot }
}
}
}
@@ -5,4 +5,4 @@ contract c {
}
}
// ----
// TypeError 1408: (75-76): Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.
// TypeError 1408: (75-76): Only local variables are supported. To access storage variables, use the .slot and .offset suffixes.
@@ -1,15 +1,11 @@
contract C {
function f() public pure {
assembly {
let x_offset := 1
let x_slot := 1
let _offset := 1
let _slot := 1
let x.offset := 1
let x.slot := 1
}
}
}
// ----
// DeclarationError 9155: (79-87): In variable declarations _slot and _offset can not be used as a suffix.
// DeclarationError 9155: (109-115): In variable declarations _slot and _offset can not be used as a suffix.
// DeclarationError 9155: (137-144): In variable declarations _slot and _offset can not be used as a suffix.
// DeclarationError 9155: (166-171): In variable declarations _slot and _offset can not be used as a suffix.
// DeclarationError 3927: (79-87): User-defined identifiers in inline assembly cannot contain '.'.
// DeclarationError 3927: (109-115): User-defined identifiers in inline assembly cannot contain '.'.
@@ -16,4 +16,3 @@ contract B {
// ----
// DeclarationError 3859: (b:105-106): This declaration shadows a declaration outside the inline assembly block.
// DeclarationError 3927: (b:128-131): User-defined identifiers in inline assembly cannot contain '.'.
// DeclarationError 3859: (b:128-131): The prefix of this declaration conflicts with a declaration outside the inline assembly block.
@@ -11,6 +11,4 @@ contract C {
}
// ----
// DeclarationError 3927: (115-118): User-defined identifiers in inline assembly cannot contain '.'.
// DeclarationError 3859: (115-118): The prefix of this declaration conflicts with a declaration outside the inline assembly block.
// DeclarationError 3927: (140-143): User-defined identifiers in inline assembly cannot contain '.'.
// DeclarationError 3859: (140-143): The prefix of this declaration conflicts with a declaration outside the inline assembly block.
@@ -8,4 +8,4 @@ contract C {
}
}
// ----
// TypeError 9068: (118-119): You have to use the _slot or _offset suffix to access storage reference variables.
// TypeError 9068: (118-119): You have to use the .slot or .offset suffix to access storage reference variables.
@@ -3,10 +3,10 @@ contract C {
fallback() external {
uint[] storage y = x;
assembly {
y_slot := 1
y_offset := 2
y.slot := 1
y.offset := 2
}
}
}
// ----
// TypeError 9739: (138-146): Only _slot can be assigned to.
// TypeError 9739: (138-146): Only .slot can be assigned to.
@@ -2,8 +2,8 @@ contract C {
uint[] x;
fallback() external {
assembly {
x_slot := 1
x_offset := 2
x.slot := 1
x.offset := 2
}
}
}
@@ -1,9 +1,9 @@
contract C {
function f() public pure {
assembly {
let x := _offset
let x := .offset
}
}
}
// ----
// DeclarationError 4794: (84-91): In variable names _slot and _offset can only be used as a suffix.
// ParserError 1856: (84-85): Literal or identifier expected.
@@ -1,9 +1,9 @@
contract C {
function f() public pure {
assembly {
let x := _slot
let x := .slot
}
}
}
// ----
// DeclarationError 4794: (84-89): In variable names _slot and _offset can only be used as a suffix.
// ParserError 1856: (84-85): Literal or identifier expected.
@@ -3,8 +3,8 @@ contract C {
fallback() external {
uint[] storage y = x;
assembly {
pop(y_slot)
pop(y_offset)
pop(y.slot)
pop(y.offset)
}
}
}
@@ -0,0 +1,13 @@
contract C {
uint[] x;
fallback() external {
uint[] storage y = x;
assembly {
pop(y_slot)
pop(y_offset)
}
}
}
// ----
// DeclarationError 9467: (118-124): Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables.
// DeclarationError 9467: (142-150): Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables.
@@ -0,0 +1,14 @@
contract C {
uint[] x;
fallback() external {
uint y_slot = 2;
uint y_offset = 3;
uint[] storage y = x;
assembly {
pop(y_slot)
pop(y_offset)
}
y[0] = 2;
}
}
// ----
@@ -1,9 +1,9 @@
contract C {
function f() pure public {
assembly {
let x := f_slot
let x := f.slot
}
}
}
// ----
// TypeError 7944: (84-90): The suffixes _offset and _slot can only be used on storage variables.
// TypeError 7944: (84-90): The suffixes .offset and .slot can only be used on storage variables.
@@ -3,11 +3,11 @@ contract C {
fallback() external {
uint[] memory y = x;
assembly {
pop(y_slot)
pop(y_offset)
pop(y.slot)
pop(y.offset)
}
}
}
// ----
// TypeError 3622: (117-123): The suffixes _offset and _slot can only be used on storage variables.
// TypeError 3622: (141-149): The suffixes _offset and _slot can only be used on storage variables.
// TypeError 3622: (117-123): The suffixes .offset and .slot can only be used on storage variables.
// TypeError 3622: (141-149): The suffixes .offset and .slot can only be used on storage variables.
@@ -3,10 +3,10 @@ contract C {
fallback() external {
uint[] storage y = x;
assembly {
y_slot := 1
y_offset := 2
y.slot := 1
y.offset := 2
}
}
}
// ----
// TypeError 9739: (138-146): Only _slot can be assigned to.
// TypeError 9739: (138-146): Only .slot can be assigned to.
@@ -2,7 +2,7 @@ contract C {
struct S { uint x; }
S s;
function e() pure public {
assembly { mstore(keccak256(0, 20), mul(s_slot, 2)) }
assembly { mstore(keccak256(0, 20), mul(s.slot, 2)) }
}
function f() pure public {
uint x;