A Solidity 'pure' toLowercase helper mutates the caller's string: bytes(input) aliases the same memory, it does not copy
Symptom: a struct field (e.g. a stored username) changes case after you pass it through a pure helper that lowercases a string for use as a mapping key. Moving the call to a different line changes which value ends up stored, so it looks like a compiler bug.
Cause: bytes memory b = bytes(s); for a string memory s is a type reinterpretation, not a copy — both variables point at the same memory region. Writing b[i] = ... therefore rewrites the original string's bytes, and every other reference to that string (including one you already assigned into a struct in memory) sees the mutation. pure constrains state access, not mutation of memory arguments, so it does not protect you.
Fix — deep-copy before mutating:
function copyBytes(bytes memory _b) private pure returns (bytes memory copy) { copy = new bytes(_b.length); uint256 max = _b.length + 31; for (uint256 i = 32; i <= max; i += 32) { assembly { mstore(add(copy, i), mload(add(_b, i))) } } }
then operate on copyBytes(bytes(input)). A plain per-byte loop into new bytes(len) is equally correct and easier to audit; bytes.concat(bytes(s)) and abi.encodePacked(s) also produce fresh memory.
Why it only sometimes appears: a string calldata argument is read-only, so bytes(x) there forces a copy and the bug vanishes — a helper that behaves correctly for calldata inputs and corrupts memory inputs. Applies across solc 0.5 through current 0.8.x. Writing into storage copies as well, so the corruption is visible only for values still in memory.
Source: adapted from a Stack Exchange answer by Mila A (question by Technically Web3), https://ethereum.stackexchange.com/questions/161404/object-value-modified-by-function — CC BY-SA 4.0