Merge pull request #8374 from ethereum/writeAccessToSlot

Allow access to ``_slot`` for local storage pointer variables.
This commit is contained in:
chriseth 2020-02-25 22:11:13 +01:00 committed by GitHub
commit c5a0a434e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 7 deletions

View File

@ -1,6 +1,7 @@
### 0.6.4 (unreleased)
Language Features:
* Inline Assembly: Allow assigning to `_slot` of local storage variable pointers.
Compiler Features:

View File

@ -172,6 +172,11 @@ 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,
though.
Things to Avoid
@ -225,4 +230,3 @@ 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.

View File

@ -677,11 +677,21 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
return size_t(-1);
}
else if (_context != yul::IdentifierContext::RValue)
else if (_context == yul::IdentifierContext::LValue)
{
m_errorReporter.typeError(_identifier.location, "Storage variables cannot be assigned to.");
if (var->isStateVariable())
{
m_errorReporter.typeError(_identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\".");
return size_t(-1);
}
else if (ref->second.isOffset)
{
m_errorReporter.typeError(_identifier.location, "Only _slot can be assigned to.");
return size_t(-1);
}
else
solAssert(ref->second.isSlot, "");
}
}
else if (!var->isConstant() && var->isStateVariable())
{

View File

@ -781,7 +781,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
else
{
// lvalue context
solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
solAssert(!ref->second.isOffset, "");
auto variable = dynamic_cast<VariableDeclaration const*>(decl);
solAssert(
!!variable && m_context.isLocalVariable(variable),

View File

@ -0,0 +1,33 @@
contract C {
struct S {
uint a;
uint b;
}
mapping(uint => S) public mappingAccess;
function data() internal view returns (S storage _data) {
// We need to assign it from somewhere, otherwise we would
// get an "uninitialized access" error.
_data = mappingAccess[20];
bytes32 slot = keccak256(abi.encode(uint(1), uint(0)));
assembly {
_data_slot := slot
}
}
function set(uint x) public {
data().a = x;
}
function get() public view returns (uint) {
return data().a;
}
}
// ----
// get() -> 0
// mappingAccess(uint256): 1 -> 0, 0
// set(uint256): 4
// get() -> 4
// mappingAccess(uint256): 1 -> 4, 0

View File

@ -9,5 +9,4 @@ contract C {
}
}
// ----
// TypeError: (114-120): Storage variables cannot be assigned to.
// TypeError: (138-146): Storage variables cannot be assigned to.
// TypeError: (138-146): Only _slot can be assigned to.

View File

@ -0,0 +1,12 @@
contract C {
uint[] x;
fallback() external {
assembly {
x_slot := 1
x_offset := 2
}
}
}
// ----
// TypeError: (84-90): State variables cannot be assigned to - you have to use "sstore()".
// TypeError: (108-116): State variables cannot be assigned to - you have to use "sstore()".

View File

@ -0,0 +1,12 @@
contract C {
uint[] x;
fallback() external {
uint[] storage y = x;
assembly {
y_slot := 1
y_offset := 2
}
}
}
// ----
// TypeError: (138-146): Only _slot can be assigned to.