solidity icon indicating copy to clipboard operation
solidity copied to clipboard

Inconsistent view/pure semantic restrictions related to storage pointers

Open Philogy opened this issue 10 months ago • 0 comments

Description

  • The compiler requires internal functions that index into storage mappings just to return a storage pointer not read the actual contents be marked view.
  • 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 accepts pure
  • Furthermore a public function that calls the pure, storage pointer returning method just to publicly return the struct as a memory struct (implicitly reading storage) the compiler does not require said function to be marked view

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

Philogy avatar Feb 23 '25 20:02 Philogy