diff --git a/Changelog.md b/Changelog.md index 9981ea25f..6a829845a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.6.4 (unreleased) Language Features: + * Inline Assembly: Allow assigning to `_slot` of local storage variable pointers. Compiler Features: diff --git a/docs/assembly.rst b/docs/assembly.rst index 67c1ab6f6..7e498cd5a 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -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. - diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index e41425c6e..2f9526f87 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -677,10 +677,20 @@ 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."); - return size_t(-1); + 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()) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 298437b1d..a99aefb5f 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -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(decl); solAssert( !!variable && m_context.isLocalVariable(variable), diff --git a/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol b/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol new file mode 100644 index 000000000..4f3be7489 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol @@ -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 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment.sol index 164265781..91362d719 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment.sol @@ -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. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment_statevar.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment_statevar.sol new file mode 100644 index 000000000..d37008774 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment_statevar.sol @@ -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()". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_slot_assign.yul b/test/libsolidity/syntaxTests/inlineAssembly/storage_slot_assign.yul new file mode 100644 index 000000000..91362d719 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_slot_assign.yul @@ -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.