solidity
solidity copied to clipboard
Inconsistent view/pure semantic restrictions related to storage pointers
Description
- The compiler requires
internalfunctions that index into storage mappings just to return a storage pointer not read the actual contents be markedview. - When manually creating & assigning the slot of a struct storage pointer via inline assembly the compiler does not impose the same constraint of requiring the method to be marked
view, it acceptspure - Furthermore a
publicfunction that calls thepure, storage pointer returning method just to publicly return the struct as amemorystruct (implicitly reading storage) the compiler does not require said function to be markedview
Intuitively one would expect that neither creation of the storage pointer (assembly or vanilla Solidity) should be view as it does not actually read storage yet, simply create a stack variable based on some inputs. However casting a storage pointer to a memory struct which requires reading the storage pointer should be marked as an environment touching operation.
Environment
- Compiler version: v0.8.25 / v0.8.28
- Compilation pipeline (legacy, IR, EOF): legacy & via-IR
- Target EVM version (as per compiler settings): paris, shanghai, cancun
- Framework/IDE (e.g. Foundry, Hardhat, Remix): foundry
- EVM execution environment / backend / blockchain client: standard
- Operating system: macOS (apple silicon)
Steps to Reproduce
Example
struct TestStruct {
uint256 value;
}
mapping(bytes32 => TestStruct) _map;
/// @dev Compiler ❌ ERROR: At least visibility of *view* required.
function _get_no_assembly(bytes32 key) internal pure returns (TestStruct storage state) {
state = _map[key];
}
/// @dev Compiler accepts ✅
function _get_assembly(bytes32 key) internal pure returns (TestStruct storage state) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, _map.slot)
state.slot := keccak256(0x00, 0x40)
}
}
/// @dev Implicit storage read passes as *pure*
function pub_get(bytes32 key) public pure returns (TestStruct memory) {
return _get_assembly(key);
}