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
commit cf189a3285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 178 additions and 146 deletions

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()`.

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.

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 ;

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.

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."
);
}

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)

View File

@ -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"
},

View File

@ -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) }
}
}

View File

@ -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,

View File

@ -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)
}
}
}

View File

@ -6,7 +6,7 @@ contract C {
data.push(123);
delete data;
assembly {
ret := sload(data_slot)
ret := sload(data.slot)
}
}

View File

@ -8,7 +8,7 @@ contract C {
}
x = m;
assembly {
r := sload(x_slot)
r := sload(x.slot)
}
}
}

View File

@ -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);

View File

@ -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()
}

View File

@ -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;

View File

@ -13,7 +13,7 @@ contract C {
bytes32 slot = keccak256(abi.encode(uint(1), uint(0)));
assembly {
_data_slot := slot
_data.slot := slot
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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)));
}

View File

@ -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)));
}

View File

@ -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)) {

View File

@ -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;

View File

@ -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];

View File

@ -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];

View File

@ -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;

View File

@ -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];

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -7,7 +7,7 @@ contract C {
assembly {
function f() { return(0, 0) }
f()
c_slot := s_slot
c.slot := s.slot
}
c;
}

View File

@ -4,7 +4,7 @@ contract C {
function f() internal pure {
S storage c;
assembly {
c_slot := s_slot
c.slot := s.slot
}
c;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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 }
}
}
}

View File

@ -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) {} {}
}
}
}

View File

@ -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 }
}
}
}

View File

@ -6,7 +6,7 @@ contract C {
assembly {
function f() { return(0, 0) }
f()
c_slot := s_slot
c.slot := s.slot
}
}
}

View File

@ -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
}
}
}

View File

@ -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) }
}
}

View File

@ -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 }
}
}
}

View File

@ -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 }
}
}
}

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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 }
}
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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 }
}
}
}

View File

@ -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.

View File

@ -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 '.'.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -2,8 +2,8 @@ contract C {
uint[] x;
fallback() external {
assembly {
x_slot := 1
x_offset := 2
x.slot := 1
x.offset := 2
}
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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)
}
}
}

View File

@ -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.

View File

@ -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;
}
}
// ----

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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;