From 7667afcfb8d9b6843486c415474275790cbe511f Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 16 Jan 2026 13:30:54 -0500 Subject: [PATCH 01/73] CI (zellic-audit): run semantic tests with optimizer on for 200 runs as well (#103) --- .github/workflows/test.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9f82c6006..901c263f6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,7 @@ on: pull_request: branches: - seismic + - zellic-audit concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -71,7 +72,7 @@ jobs: cmake --build . --config Debug -j $(nproc) - name: Run soltest run: ./scripts/soltest.sh - - name: Run semantic tests + - name: Run semantic tests (no optimizer) run: | set -euo pipefail SSOLC_EXEC="$GITHUB_WORKSPACE/build/solc/solc" @@ -79,3 +80,11 @@ jobs: [ -x "$SEISMIC_REVME_EXEC" ] || { echo "Error: revme executable not found at $SEISMIC_REVME_EXEC"; exit 1; } SEMANTIC_TESTS_DIR="$GITHUB_WORKSPACE/test/libsolidity/semanticTests" RUST_LOG=info RUST_BACKTRACE=1 $SEISMIC_REVME_EXEC semantics --keep-going -s "$SSOLC_EXEC" -t "$SEMANTIC_TESTS_DIR" 2>&1 + - name: Run semantic tests (with optimizer) + run: | + set -euo pipefail + SSOLC_EXEC="$GITHUB_WORKSPACE/build/solc/solc" + [ -x "$SSOLC_EXEC" ] || { echo "Error: solc not found at $SSOLC_EXEC"; exit 1; } + [ -x "$SEISMIC_REVME_EXEC" ] || { echo "Error: revme executable not found at $SEISMIC_REVME_EXEC"; exit 1; } + SEMANTIC_TESTS_DIR="$GITHUB_WORKSPACE/test/libsolidity/semanticTests" + RUST_LOG=info RUST_BACKTRACE=1 $SEISMIC_REVME_EXEC semantics --keep-going -s "$SSOLC_EXEC" -t "$SEMANTIC_TESTS_DIR" --optimize --optimizer-runs 200 2>&1 From bca3310f3b9327dc501ca899391cc78d40cb0c98 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 16 Jan 2026 14:29:53 -0500 Subject: [PATCH 02/73] Fix: in UnusedStorageEliminator, CSTORE is indeed a valid storage write (#108) --- libyul/optimiser/UnusedStoreEliminator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index 6f2f1ee93..0fa74dbda 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -158,7 +158,7 @@ void UnusedStoreEliminator::visit(Statement const& _statement) // both by querying a combination of semantic information and by listing the instructions. // This way the assert below should be triggered on any change. using evmasm::SemanticInformation; - bool isStorageWrite = (*instruction == Instruction::SSTORE); + bool isStorageWrite = (*instruction == Instruction::SSTORE || *instruction == Instruction::CSTORE); bool isMemoryWrite = *instruction == Instruction::EXTCODECOPY || *instruction == Instruction::CODECOPY || From dd83ecc72c51b5fa07d34e9eea55853dab1e0086 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 16 Jan 2026 14:40:04 -0500 Subject: [PATCH 03/73] Test: GasMeter should know CLOAD/CSTORE's fix gas costs (#109) --- test/CMakeLists.txt | 1 + test/libevmasm/GasMeterTest.cpp | 134 ++++++++++++++++++++++++++++++++ test/libsolidity/GasMeter.cpp | 59 ++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 test/libevmasm/GasMeterTest.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b0cd991f7..c81538368 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -53,6 +53,7 @@ set(libevmasm_sources libevmasm/Assembler.cpp libevmasm/EVMAssemblyTest.cpp libevmasm/EVMAssemblyTest.h + libevmasm/GasMeterTest.cpp libevmasm/Optimiser.cpp libevmasm/PlainAssemblyParser.cpp libevmasm/PlainAssemblyParser.h diff --git a/test/libevmasm/GasMeterTest.cpp b/test/libevmasm/GasMeterTest.cpp new file mode 100644 index 000000000..b370bf2aa --- /dev/null +++ b/test/libevmasm/GasMeterTest.cpp @@ -0,0 +1,134 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Unit tests for the gas meter for CLOAD/CSTORE opcodes. + */ + +#include +#include +#include + +#include + +using namespace solidity::evmasm; +using namespace solidity::langutil; + +namespace solidity::evmasm::test +{ + +BOOST_AUTO_TEST_SUITE(GasMeterTest) + +BOOST_AUTO_TEST_CASE(cload_gas_cost) +{ + // Test that CLOAD has the expected fixed gas cost + auto evmVersion = EVMVersion::mercury(); + auto state = std::make_shared(); + GasMeter meter(state, evmVersion); + + AssemblyItem cloadItem(Instruction::CLOAD); + auto gas = meter.estimateMax(cloadItem, false); + + // CLOAD should cost coldSloadCost = 2100 + // BOOST_CHECK_EQUAL(gas.value, GasCosts::cloadGas); + BOOST_CHECK_EQUAL(gas.value, 2100); +} + +BOOST_AUTO_TEST_CASE(cstore_gas_cost_berlin) +{ + // Test that CSTORE has the expected fixed gas cost for Berlin+ + auto evmVersion = EVMVersion::mercury(); + auto state = std::make_shared(); + GasMeter meter(state, evmVersion); + + AssemblyItem cstoreItem(Instruction::CSTORE); + auto gas = meter.estimateMax(cstoreItem, false); + + // CSTORE for Berlin+ should cost: warmStorageReadCost(100) + 20000 + coldSloadCost-warmStorageReadCost(2000) + // = 100 + 20000 + 2000 = 22100 + // BOOST_CHECK_EQUAL(gas.value, GasCosts::cstoreGas(evmVersion)); + BOOST_CHECK_EQUAL(gas.value, 22100); +} + +BOOST_AUTO_TEST_CASE(cstore_gas_cost_istanbul) +{ + // Test that CSTORE has the expected fixed gas cost for Istanbul + auto evmVersion = EVMVersion::istanbul(); + auto state = std::make_shared(); + GasMeter meter(state, evmVersion); + + AssemblyItem cstoreItem(Instruction::CSTORE); + auto gas = meter.estimateMax(cstoreItem, false); + + // CSTORE for Istanbul should cost: 800 + 20000 + 2000 = 22800 + // BOOST_CHECK_EQUAL(gas.value, GasCosts::cstoreGas(evmVersion)); + BOOST_CHECK_EQUAL(gas.value, 22800); +} + +BOOST_AUTO_TEST_CASE(cload_cstore_sequence) +{ + // Test gas estimation for a sequence of CLOAD and CSTORE + auto evmVersion = EVMVersion::mercury(); + auto state = std::make_shared(); + GasMeter meter(state, evmVersion); + + // Simulate: PUSH1 0, PUSH1 42, CSTORE, PUSH1 0, CLOAD + AssemblyItem push1_0(Instruction::PUSH1); + AssemblyItem push1_42(Instruction::PUSH1); + AssemblyItem cstoreItem(Instruction::CSTORE); + AssemblyItem cloadItem(Instruction::CLOAD); + + GasMeter::GasConsumption totalGas(0); + totalGas += meter.estimateMax(push1_0, false); + totalGas += meter.estimateMax(push1_42, false); + totalGas += meter.estimateMax(cstoreItem, false); + totalGas += meter.estimateMax(push1_0, false); + totalGas += meter.estimateMax(cloadItem, false); + + // Should be: 3 + 3 + 22100 + 3 + 2100 = 24209 + BOOST_CHECK_EQUAL(totalGas.value, 24209); +} + +BOOST_AUTO_TEST_CASE(cload_not_infinite) +{ + // Ensure CLOAD doesn't return infinite gas + auto evmVersion = EVMVersion::mercury(); + auto state = std::make_shared(); + GasMeter meter(state, evmVersion); + + AssemblyItem cloadItem(Instruction::CLOAD); + auto gas = meter.estimateMax(cloadItem, false); + + BOOST_CHECK(!gas.isInfinite); +} + +BOOST_AUTO_TEST_CASE(cstore_not_infinite) +{ + // Ensure CSTORE doesn't return infinite gas + auto evmVersion = EVMVersion::mercury(); + auto state = std::make_shared(); + GasMeter meter(state, evmVersion); + + AssemblyItem cstoreItem(Instruction::CSTORE); + auto gas = meter.estimateMax(cstoreItem, false); + + BOOST_CHECK(!gas.isInfinite); +} + +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 9012ad083..1b2783a4d 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -447,6 +447,65 @@ BOOST_AUTO_TEST_CASE( testRunTimeGas("overlap_full()", {encodeArgs()}); } +BOOST_AUTO_TEST_CASE(cload_cstore_gas_estimation) +{ + char const* sourceCode = R"( + contract test { + suint256 private data; + + function testCStore() public { + assembly { + cstore(0, 42) + } + } + + function testCLoad() public returns (uint256 value) { + assembly { + value := cload(0) + } + } + + function testBoth() public returns (uint256 value) { + assembly { + cstore(1, 123) + value := cload(1) + } + } + } + )"; + testCreationTimeGas(sourceCode); + testRunTimeGas("testCStore()", {encodeArgs()}); + testRunTimeGas("testCLoad()", {encodeArgs()}); + testRunTimeGas("testBoth()", {encodeArgs()}); +} + +BOOST_AUTO_TEST_CASE(cload_cstore_with_shielded_types) +{ + char const* sourceCode = R"( + contract test { + suint256 private data; + suint256 private data2; + + function setData(suint256 x) public { + data = x; + } + + function getData() public view returns (suint256) { + return data; + } + + function setBoth(suint256 x, suint256 y) public { + data = x; + data2 = y; + } + } + )"; + testCreationTimeGas(sourceCode); + testRunTimeGas("setData(uint256)", {encodeArgs(5)}); + testRunTimeGas("getData()", {encodeArgs()}); + testRunTimeGas("setBoth(uint256,uint256)", {encodeArgs(5, 10)}); +} + BOOST_AUTO_TEST_SUITE_END() } From 9165989d3b4296d598e2fbbe3a7a4fdc9877c09b Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 16 Jan 2026 14:59:06 -0500 Subject: [PATCH 04/73] Fix: GasMeter should handle CLOAD/CSTORE operations (#104) --- libevmasm/GasMeter.cpp | 6 ++++++ libevmasm/GasMeter.h | 19 +++++++++++++++++++ test/libevmasm/GasMeterTest.cpp | 6 +++--- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index fde0819da..ba39053a4 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -79,6 +79,12 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::SLOAD: gas = GasCosts::sloadGas(m_evmVersion); break; + case Instruction::CLOAD: + gas = GasCosts::cloadGas; + break; + case Instruction::CSTORE: + gas = GasCosts::cstoreGas(m_evmVersion); + break; case Instruction::RETURN: case Instruction::REVERT: gas = runGas(_item.instruction(), m_evmVersion); diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index 286999e5c..d73964451 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -121,6 +121,25 @@ namespace GasCosts else return 20; } + /// Fixed gas cost for CSTORE instruction to prevent information leakage. + /// Corresponds to CSTORE_FIXED_GAS in seismic-revm. + static unsigned const cstoreFixedGas = 20000; + /// Fixed gas cost for CLOAD (confidential storage load) + /// Uses flat cost to prevent information leakage + static unsigned const cloadGas = coldSloadCost; + /// Fixed gas cost for CSTORE (confidential storage store) + /// Uses flat cost to prevent information leakage + inline unsigned cstoreGas(langutil::EVMVersion _evmVersion) + { + // CSTORE gas = static_sstore_cost + CSTORE_FIXED_GAS + COLD_SLOAD_COST_ADDITIONAL + // For Berlin+: WARM_STORAGE_READ_COST + cstoreFixedGas + COLD_SLOAD_COST_ADDITIONAL = 100 + 20000 + 2000 = 22100 + if (_evmVersion >= langutil::EVMVersion::berlin()) + return warmStorageReadCost + cstoreFixedGas + (coldSloadCost - warmStorageReadCost); + else if (_evmVersion >= langutil::EVMVersion::istanbul()) + return 800 + cstoreFixedGas + (coldSloadCost - warmStorageReadCost); + else + return sstoreResetGas + cstoreFixedGas + (coldSloadCost - warmStorageReadCost); + } inline unsigned balanceGas(langutil::EVMVersion _evmVersion) { if (_evmVersion >= langutil::EVMVersion::berlin()) diff --git a/test/libevmasm/GasMeterTest.cpp b/test/libevmasm/GasMeterTest.cpp index b370bf2aa..daaa4b403 100644 --- a/test/libevmasm/GasMeterTest.cpp +++ b/test/libevmasm/GasMeterTest.cpp @@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE(cload_gas_cost) auto gas = meter.estimateMax(cloadItem, false); // CLOAD should cost coldSloadCost = 2100 - // BOOST_CHECK_EQUAL(gas.value, GasCosts::cloadGas); + BOOST_CHECK_EQUAL(gas.value, GasCosts::cloadGas); BOOST_CHECK_EQUAL(gas.value, 2100); } @@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(cstore_gas_cost_berlin) // CSTORE for Berlin+ should cost: warmStorageReadCost(100) + 20000 + coldSloadCost-warmStorageReadCost(2000) // = 100 + 20000 + 2000 = 22100 - // BOOST_CHECK_EQUAL(gas.value, GasCosts::cstoreGas(evmVersion)); + BOOST_CHECK_EQUAL(gas.value, GasCosts::cstoreGas(evmVersion)); BOOST_CHECK_EQUAL(gas.value, 22100); } @@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(cstore_gas_cost_istanbul) auto gas = meter.estimateMax(cstoreItem, false); // CSTORE for Istanbul should cost: 800 + 20000 + 2000 = 22800 - // BOOST_CHECK_EQUAL(gas.value, GasCosts::cstoreGas(evmVersion)); + BOOST_CHECK_EQUAL(gas.value, GasCosts::cstoreGas(evmVersion)); BOOST_CHECK_EQUAL(gas.value, 22800); } From 15ddfef815205b390b4f856157e1d190f22a90c7 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 20 Jan 2026 14:44:13 -0500 Subject: [PATCH 05/73] Failing tests: CSE applies across confidential domain to remove sload from a private slot (#110) --- test/libevmasm/Optimiser.cpp | 102 ++++++++++++++++++ .../cstore_cload_then_sload_cse.sol | 13 +++ .../cstore_then_cload_can_optimize.sol | 10 ++ .../cstore_then_sload_no_optimize.sol | 10 ++ .../sstore_then_cload_no_optimize.sol | 10 ++ .../sstore_then_sload_can_optimize.sol | 10 ++ 6 files changed, 155 insertions(+) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/cstore_cload_then_sload_cse.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/cstore_then_cload_can_optimize.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_no_optimize.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/sstore_then_cload_no_optimize.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/sstore_then_sload_can_optimize.sol diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 91115e8c8..9b1fc1090 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -2521,6 +2521,108 @@ BOOST_AUTO_TEST_CASE(inliner_invalid) } +BOOST_AUTO_TEST_CASE(cse_sstore_then_cload_no_optimization) +{ + AssemblyItems input{ + u256(0x42), + u256(0), + Instruction::SSTORE, + u256(0), + Instruction::CLOAD + }; + // Optimizer does stack manipulation but keeps CLOAD (doesn't replace with SSTORE value) + checkCSE(input, { + u256(0x42), + u256(0), + Instruction::SWAP1, + Instruction::DUP2, + Instruction::SSTORE, + Instruction::CLOAD + }); +} + +BOOST_AUTO_TEST_CASE(cse_cstore_then_sload_no_optimization) +{ + AssemblyItems input{ + u256(0x42), + u256(0), + Instruction::CSTORE, + u256(0), + Instruction::SLOAD + }; + // Optimizer does stack manipulation but keeps SLOAD (doesn't replace with CSTORE value) + checkCSE(input, { + u256(0x42), + u256(0), + Instruction::SWAP1, + Instruction::DUP2, + Instruction::CSTORE, + Instruction::SLOAD + }); +} + +BOOST_AUTO_TEST_CASE(cse_sstore_cload_different_slots_no_optimization) +{ + AssemblyItems input{ + u256(0x42), + u256(0), + Instruction::SSTORE, + u256(1), + Instruction::CLOAD + }; + checkCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_cstore_sload_different_slots_no_optimization) +{ + AssemblyItems input{ + u256(0x42), + u256(0), + Instruction::CSTORE, + u256(1), + Instruction::SLOAD + }; + checkCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_sstore_sload_same_slot_can_optimize) +{ + AssemblyItems input{ + u256(0x42), + u256(0), + Instruction::SSTORE, + u256(0), + Instruction::SLOAD + }; + // Optimizer eliminates SLOAD and keeps value on stack + checkCSE(input, { + u256(0x42), + u256(0), + Instruction::DUP2, + Instruction::SWAP1, + Instruction::SSTORE + }); +} + +BOOST_AUTO_TEST_CASE(cse_cstore_cload_same_slot_can_optimize) +{ + AssemblyItems input{ + u256(0x42), + u256(0), + Instruction::CSTORE, + u256(0), + Instruction::CLOAD + }; + // Optimizer eliminates CLOAD and keeps value on stack + checkCSE(input, { + u256(0x42), + u256(0), + Instruction::DUP2, + Instruction::SWAP1, + Instruction::CSTORE + }); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces diff --git a/test/libsolidity/semanticTests/inlineAssembly/cstore_cload_then_sload_cse.sol b/test/libsolidity/semanticTests/inlineAssembly/cstore_cload_then_sload_cse.sol new file mode 100644 index 000000000..10b690909 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/cstore_cload_then_sload_cse.sol @@ -0,0 +1,13 @@ +contract C { + function test() external returns (uint256) { + assembly { + cstore(0, 0x1337) + let a := cload(0) + let b := sload(0) + mstore(0, add(a, b)) + return(0, 0x20) + } + } +} +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/semanticTests/inlineAssembly/cstore_then_cload_can_optimize.sol b/test/libsolidity/semanticTests/inlineAssembly/cstore_then_cload_can_optimize.sol new file mode 100644 index 000000000..116e45079 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/cstore_then_cload_can_optimize.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (uint ret) { + assembly { + cstore(0, 42) + ret := cload(0) + } + } +} +// ---- +// f() -> 42 diff --git a/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_no_optimize.sol b/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_no_optimize.sol new file mode 100644 index 000000000..7119b9ed8 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_no_optimize.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (uint ret) { + assembly { + cstore(0, 42) + ret := sload(0) + } + } +} +// ---- +// f() -> FAILURE diff --git a/test/libsolidity/semanticTests/inlineAssembly/sstore_then_cload_no_optimize.sol b/test/libsolidity/semanticTests/inlineAssembly/sstore_then_cload_no_optimize.sol new file mode 100644 index 000000000..b7bdab03d --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/sstore_then_cload_no_optimize.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (uint ret) { + assembly { + sstore(0, 42) + ret := cload(0) + } + } +} +// ---- +// f() -> FAILURE diff --git a/test/libsolidity/semanticTests/inlineAssembly/sstore_then_sload_can_optimize.sol b/test/libsolidity/semanticTests/inlineAssembly/sstore_then_sload_can_optimize.sol new file mode 100644 index 000000000..e6c1f2463 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/sstore_then_sload_can_optimize.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (uint ret) { + assembly { + sstore(0, 42) + ret := sload(0) + } + } +} +// ---- +// f() -> 42 From 88bb1dc26dfe34ebb4b4ce7e0192efef33c33686 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 20 Jan 2026 14:44:52 -0500 Subject: [PATCH 06/73] Fix: CSE applies across confidential domain to remove sload from a private slot (#111) --- libevmasm/GasMeter.cpp | 3 ++- libevmasm/KnownState.cpp | 50 +++++++++++++++++++++++++++------------- libevmasm/KnownState.h | 15 ++++++++++-- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index ba39053a4..81d92fec0 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -69,7 +69,8 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ ExpressionClasses::Id value = m_state->relativeStackElement(-1); if (classes.knownZero(value) || ( m_state->storageContent().count(slot) && - classes.knownNonZero(m_state->storageContent().at(slot)) + classes.knownNonZero(m_state->storageContent().at(slot).value) && + !m_state->storageContent().at(slot).is_private )) gas = GasCosts::totalSstoreResetGas(m_evmVersion); //@todo take refunds into account else diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 72b9fdb84..da3a052af 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -68,10 +68,24 @@ std::ostream& KnownState::stream(std::ostream& _out) const _out << "Storage:" << std::endl; for (auto const& it: m_storageContent) { - _out << " "; - streamExpressionClass(_out, it.first); - _out << ": "; - streamExpressionClass(_out, it.second); + if (!it.second.is_private) + { + _out << " "; + streamExpressionClass(_out, it.first); + _out << ": "; + streamExpressionClass(_out, it.second.value); + } + } + _out << "Shielded Storage:" << std::endl; + for (auto const& it: m_storageContent) + { + if (it.second.is_private) + { + _out << " "; + streamExpressionClass(_out, it.first); + _out << ": "; + streamExpressionClass(_out, it.second.value); + } } _out << "Memory:" << std::endl; for (auto const& it: m_memoryContent) @@ -334,7 +348,7 @@ KnownState::StoreOperation KnownState::storeInStorage( langutil::DebugData::ConstPtr _debugData ) { - if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value) + if (m_storageContent.count(_slot) && m_storageContent[_slot] == FlaggedStorage{_value, false}) // do not execute the storage if we know that the value is already there return StoreOperation(); m_sequenceNumber++; @@ -343,14 +357,14 @@ KnownState::StoreOperation KnownState::storeInStorage( // operation will not destroy the knowledge. Specifically, we copy storage locations we know // are different from _slot or locations where we know that the stored value is equal to _value. for (auto const& storageItem: m_storageContent) - if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value) + if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || storageItem.second.value == _value) storageContents.insert(storageItem); m_storageContent = std::move(storageContents); AssemblyItem item(Instruction::SSTORE, std::move(_debugData)); Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber); StoreOperation operation{StoreOperation::Storage, _slot, m_sequenceNumber, id}; - m_storageContent[_slot] = _value; + m_storageContent[_slot] = {_value, false}; // increment a second time so that we get unique sequence numbers for writes m_sequenceNumber++; @@ -363,7 +377,7 @@ KnownState::StoreOperation KnownState::storeInShieldedStorage( langutil::DebugData::ConstPtr _debugData ) { - if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value) + if (m_storageContent.count(_slot) && m_storageContent[_slot] == FlaggedStorage{_value, true}) // do not execute the storage if we know that the value is already there return StoreOperation(); m_sequenceNumber++; @@ -372,14 +386,14 @@ KnownState::StoreOperation KnownState::storeInShieldedStorage( // operation will not destroy the knowledge. Specifically, we copy storage locations we know // are different from _slot or locations where we know that the stored value is equal to _value. for (auto const& storageItem: m_storageContent) - if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value) + if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || storageItem.second.value == _value) storageContents.insert(storageItem); m_storageContent = std::move(storageContents); AssemblyItem item(Instruction::CSTORE, std::move(_debugData)); Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber); StoreOperation operation{StoreOperation::Storage, _slot, m_sequenceNumber, id}; - m_storageContent[_slot] = _value; + m_storageContent[_slot] = {_value, true}; // increment a second time so that we get unique sequence numbers for writes m_sequenceNumber++; @@ -388,20 +402,24 @@ KnownState::StoreOperation KnownState::storeInShieldedStorage( ExpressionClasses::Id KnownState::loadFromStorage(Id _slot, langutil::DebugData::ConstPtr _debugData) { - if (m_storageContent.count(_slot)) - return m_storageContent.at(_slot); + if (m_storageContent.count(_slot) && !m_storageContent.at(_slot).is_private) + return m_storageContent.at(_slot).value; AssemblyItem item(Instruction::SLOAD, std::move(_debugData)); - return m_storageContent[_slot] = m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); + Id value = m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); + m_storageContent[_slot] = {value, false}; + return value; } ExpressionClasses::Id KnownState::loadFromShieldedStorage(Id _slot, langutil::DebugData::ConstPtr _debugData) { - if (m_storageContent.count(_slot)) - return m_storageContent.at(_slot); + if (m_storageContent.count(_slot) && m_storageContent.at(_slot).is_private) + return m_storageContent.at(_slot).value; AssemblyItem item(Instruction::CLOAD, std::move(_debugData)); - return m_storageContent[_slot] = m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); + Id value = m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); + m_storageContent[_slot] = {value, true}; + return value; } KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, langutil::DebugData::ConstPtr _debugData) diff --git a/libevmasm/KnownState.h b/libevmasm/KnownState.h index b97728f66..4847c9b30 100644 --- a/libevmasm/KnownState.h +++ b/libevmasm/KnownState.h @@ -94,6 +94,17 @@ class KnownState Id expression{std::numeric_limits::max()}; }; + struct FlaggedStorage + { + Id value; + bool is_private; + + bool operator==(FlaggedStorage const& _other) const + { + return value == _other.value && is_private == _other.is_private; + } + }; + explicit KnownState( std::shared_ptr _expressionClasses = std::make_shared() ): m_expressionClasses(std::move(_expressionClasses)) @@ -149,7 +160,7 @@ class KnownState std::map const& stackElements() const { return m_stackElements; } ExpressionClasses& expressionClasses() const { return *m_expressionClasses; } - std::map const& storageContent() const { return m_storageContent; } + std::map const& storageContent() const { return m_storageContent; } private: /// Assigns a new equivalence class to the next sequence number of the given stack element. @@ -184,7 +195,7 @@ class KnownState /// Current sequence number, this is incremented with each modification to storage or memory. unsigned m_sequenceNumber = 1; /// Knowledge about storage content. - std::map m_storageContent; + std::map m_storageContent; /// Knowledge about memory content. Keys are memory addresses, note that the values overlap /// and are not contained here if they are not completely known. std::map m_memoryContent; From 5c7547612133797accee52ab9af02aefca6c92ad Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 20 Jan 2026 18:39:35 -0500 Subject: [PATCH 07/73] fix: GenericStorageItem::setToZero should check all shielded types (#116) --- libsolidity/codegen/LValue.cpp | 8 +- .../shielded_delete_saddress/args | 1 + .../shielded_delete_saddress/input.sol | 14 + .../shielded_delete_saddress/output | 256 ++++++++++++++++++ test/cmdlineTests/shielded_delete_sbool/args | 1 + .../shielded_delete_sbool/input.sol | 14 + .../cmdlineTests/shielded_delete_sbool/output | 238 ++++++++++++++++ .../shielded_delete_sbytes32/args | 1 + .../shielded_delete_sbytes32/input.sol | 14 + .../shielded_delete_sbytes32/output | 232 ++++++++++++++++ .../cmdlineTests/shielded_delete_sint256/args | 1 + .../shielded_delete_sint256/input.sol | 14 + .../shielded_delete_sint256/output | 232 ++++++++++++++++ .../shielded_delete_suint256/args | 1 + .../shielded_delete_suint256/input.sol | 14 + .../shielded_delete_suint256/output | 232 ++++++++++++++++ 16 files changed, 1271 insertions(+), 2 deletions(-) create mode 100644 test/cmdlineTests/shielded_delete_saddress/args create mode 100644 test/cmdlineTests/shielded_delete_saddress/input.sol create mode 100644 test/cmdlineTests/shielded_delete_saddress/output create mode 100644 test/cmdlineTests/shielded_delete_sbool/args create mode 100644 test/cmdlineTests/shielded_delete_sbool/input.sol create mode 100644 test/cmdlineTests/shielded_delete_sbool/output create mode 100644 test/cmdlineTests/shielded_delete_sbytes32/args create mode 100644 test/cmdlineTests/shielded_delete_sbytes32/input.sol create mode 100644 test/cmdlineTests/shielded_delete_sbytes32/output create mode 100644 test/cmdlineTests/shielded_delete_sint256/args create mode 100644 test/cmdlineTests/shielded_delete_sint256/input.sol create mode 100644 test/cmdlineTests/shielded_delete_sint256/output create mode 100644 test/cmdlineTests/shielded_delete_suint256/args create mode 100644 test/cmdlineTests/shielded_delete_suint256/input.sol create mode 100644 test/cmdlineTests/shielded_delete_suint256/output diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 977fe57bf..6a007c1e7 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -511,13 +511,17 @@ void GenericStorageItem::setToZero(langutil::SourceLocation const&, solAssert(m_dataType->isValueType(), "Clearing of unsupported type requested: " + m_dataType->toString()); if (!_removeReference) CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); - if (m_dataType->category() == Type::Category::ShieldedInteger && m_dataType->storageBytes() == 32) + if (m_dataType->isShielded() && m_dataType->storageBytes() == 32) { - // offset should be zero. remember, shielded integers have to be 32 bytes!! + // offset should be zero. remember, shielded types have to be 32 bytes!! m_context << Instruction::POP << u256(0) << Instruction::SWAP1 << Instruction::CSTORE; } + else if (m_dataType->isShielded()) + { + solAssert(false, "Shielded types must occupy exactly 32 bytes in storage: " + m_dataType->toString()); + } else if (m_dataType->storageBytes() == 32) { // offset should be zero diff --git a/test/cmdlineTests/shielded_delete_saddress/args b/test/cmdlineTests/shielded_delete_saddress/args new file mode 100644 index 000000000..63727f0d9 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_saddress/args @@ -0,0 +1 @@ +--asm diff --git a/test/cmdlineTests/shielded_delete_saddress/input.sol b/test/cmdlineTests/shielded_delete_saddress/input.sol new file mode 100644 index 000000000..05c4dc6e8 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_saddress/input.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + saddress private sa; + + function set(saddress v) external { + sa = v; + } + + function clr() external { + delete sa; + } +} diff --git a/test/cmdlineTests/shielded_delete_saddress/output b/test/cmdlineTests/shielded_delete_saddress/output new file mode 100644 index 000000000..82f30298b --- /dev/null +++ b/test/cmdlineTests/shielded_delete_saddress/output @@ -0,0 +1,256 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= input.sol:C ======= +EVM assembly: + /* "input.sol":60:218 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "input.sol":60:218 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x0ef1346a + eq + tag_3 + jumpi + dup1 + 0xcb06bbc6 + eq + tag_4 + jumpi + tag_2: + revert(0x00, 0x00) + /* "input.sol":166:216 function clr() external {... */ + tag_3: + tag_5 + tag_6 + jump // in + tag_5: + stop + /* "input.sol":103:160 function set(saddress v) external {... */ + tag_4: + tag_7 + 0x04 + dup1 + calldatasize + sub + dup2 + add + swap1 + tag_8 + swap2 + swap1 + tag_9 + jump // in + tag_8: + tag_10 + jump // in + tag_7: + stop + /* "input.sol":166:216 function clr() external {... */ + tag_6: + /* "input.sol":207:209 sa */ + 0x00 + /* "input.sol":200:209 delete sa */ + 0x00 + swap1 + cstore + /* "input.sol":166:216 function clr() external {... */ + jump // out + /* "input.sol":103:160 function set(saddress v) external {... */ + tag_10: + /* "input.sol":152:153 v */ + dup1 + /* "input.sol":147:149 sa */ + 0x00 + /* "input.sol":147:153 sa = v */ + dup2 + 0xffffffffffffffffffffffffffffffffffffffff + and + swap1 + cstore + pop + /* "input.sol":103:160 function set(saddress v) external {... */ + pop + jump // out + /* "#utility.yul":88:205 */ + tag_14: + /* "#utility.yul":197:198 */ + 0x00 + /* "#utility.yul":194:195 */ + 0x00 + /* "#utility.yul":187:199 */ + revert + /* "#utility.yul":334:460 */ + tag_16: + /* "#utility.yul":371:378 */ + 0x00 + /* "#utility.yul":411:453 */ + 0xffffffffffffffffffffffffffffffffffffffff + /* "#utility.yul":404:409 */ + dup3 + /* "#utility.yul":400:454 */ + and + /* "#utility.yul":389:454 */ + swap1 + pop + /* "#utility.yul":334:460 */ + swap2 + swap1 + pop + jump // out + /* "#utility.yul":466:563 */ + tag_17: + /* "#utility.yul":504:511 */ + 0x00 + /* "#utility.yul":533:557 */ + tag_26 + /* "#utility.yul":551:556 */ + dup3 + /* "#utility.yul":533:557 */ + tag_16 + jump // in + tag_26: + /* "#utility.yul":522:557 */ + swap1 + pop + /* "#utility.yul":466:563 */ + swap2 + swap1 + pop + jump // out + /* "#utility.yul":569:693 */ + tag_18: + /* "#utility.yul":643:668 */ + tag_28 + /* "#utility.yul":662:667 */ + dup2 + /* "#utility.yul":643:668 */ + tag_17 + jump // in + tag_28: + /* "#utility.yul":636:641 */ + dup2 + /* "#utility.yul":633:669 */ + eq + /* "#utility.yul":623:687 */ + tag_29 + jumpi + /* "#utility.yul":683:684 */ + 0x00 + /* "#utility.yul":680:681 */ + 0x00 + /* "#utility.yul":673:685 */ + revert + /* "#utility.yul":623:687 */ + tag_29: + /* "#utility.yul":569:693 */ + pop + jump // out + /* "#utility.yul":699:840 */ + tag_19: + /* "#utility.yul":746:751 */ + 0x00 + /* "#utility.yul":784:790 */ + dup2 + /* "#utility.yul":771:791 */ + calldataload + /* "#utility.yul":762:791 */ + swap1 + pop + /* "#utility.yul":800:834 */ + tag_31 + /* "#utility.yul":828:833 */ + dup2 + /* "#utility.yul":800:834 */ + tag_18 + jump // in + tag_31: + /* "#utility.yul":699:840 */ + swap3 + swap2 + pop + pop + jump // out + /* "#utility.yul":846:1177 */ + tag_9: + /* "#utility.yul":906:912 */ + 0x00 + /* "#utility.yul":955:957 */ + 0x20 + /* "#utility.yul":943:952 */ + dup3 + /* "#utility.yul":934:941 */ + dup5 + /* "#utility.yul":930:953 */ + sub + /* "#utility.yul":926:958 */ + slt + /* "#utility.yul":923:1042 */ + iszero + tag_33 + jumpi + /* "#utility.yul":961:1040 */ + tag_34 + tag_14 + jump // in + tag_34: + /* "#utility.yul":923:1042 */ + tag_33: + /* "#utility.yul":1081:1082 */ + 0x00 + /* "#utility.yul":1106:1160 */ + tag_35 + /* "#utility.yul":1152:1159 */ + dup5 + /* "#utility.yul":1143:1149 */ + dup3 + /* "#utility.yul":1132:1141 */ + dup6 + /* "#utility.yul":1128:1150 */ + add + /* "#utility.yul":1106:1160 */ + tag_19 + jump // in + tag_35: + /* "#utility.yul":1096:1160 */ + swap2 + pop + /* "#utility.yul":1052:1170 */ + pop + /* "#utility.yul":846:1177 */ + swap3 + swap2 + pop + pop + jump // out + + auxdata: +} diff --git a/test/cmdlineTests/shielded_delete_sbool/args b/test/cmdlineTests/shielded_delete_sbool/args new file mode 100644 index 000000000..63727f0d9 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_sbool/args @@ -0,0 +1 @@ +--asm diff --git a/test/cmdlineTests/shielded_delete_sbool/input.sol b/test/cmdlineTests/shielded_delete_sbool/input.sol new file mode 100644 index 000000000..a7505d9e3 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_sbool/input.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + sbool private sb; + + function set(sbool v) external { + sb = v; + } + + function clr() external { + delete sb; + } +} diff --git a/test/cmdlineTests/shielded_delete_sbool/output b/test/cmdlineTests/shielded_delete_sbool/output new file mode 100644 index 000000000..3eaf165af --- /dev/null +++ b/test/cmdlineTests/shielded_delete_sbool/output @@ -0,0 +1,238 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= test/cmdlineTests/shielded_delete_sbool/input.sol:C ======= +EVM assembly: + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":60:212 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":60:212 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x0ef1346a + eq + tag_3 + jumpi + dup1 + 0x3a7e91e2 + eq + tag_4 + jumpi + tag_2: + revert(0x00, 0x00) + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":160:210 function clr() external {... */ + tag_3: + tag_5 + tag_6 + jump // in + tag_5: + stop + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":100:154 function set(sbool v) external {... */ + tag_4: + tag_7 + 0x04 + dup1 + calldatasize + sub + dup2 + add + swap1 + tag_8 + swap2 + swap1 + tag_9 + jump // in + tag_8: + tag_10 + jump // in + tag_7: + stop + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":160:210 function clr() external {... */ + tag_6: + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":201:203 sb */ + 0x00 + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":194:203 delete sb */ + 0x00 + swap1 + cstore + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":160:210 function clr() external {... */ + jump // out + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":100:154 function set(sbool v) external {... */ + tag_10: + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":146:147 v */ + dup1 + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":141:143 sb */ + 0x00 + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":141:147 sb = v */ + dup2 + iszero + iszero + swap1 + cstore + pop + /* "test/cmdlineTests/shielded_delete_sbool/input.sol":100:154 function set(sbool v) external {... */ + pop + jump // out + /* "#utility.yul":88:205 */ + tag_14: + /* "#utility.yul":197:198 */ + 0x00 + /* "#utility.yul":194:195 */ + 0x00 + /* "#utility.yul":187:199 */ + revert + /* "#utility.yul":334:425 */ + tag_16: + /* "#utility.yul":369:376 */ + 0x00 + /* "#utility.yul":412:417 */ + dup2 + /* "#utility.yul":405:418 */ + iszero + /* "#utility.yul":398:419 */ + iszero + /* "#utility.yul":387:419 */ + swap1 + pop + /* "#utility.yul":334:425 */ + swap2 + swap1 + pop + jump // out + /* "#utility.yul":431:549 */ + tag_17: + /* "#utility.yul":502:524 */ + tag_25 + /* "#utility.yul":518:523 */ + dup2 + /* "#utility.yul":502:524 */ + tag_16 + jump // in + tag_25: + /* "#utility.yul":495:500 */ + dup2 + /* "#utility.yul":492:525 */ + eq + /* "#utility.yul":482:543 */ + tag_26 + jumpi + /* "#utility.yul":539:540 */ + 0x00 + /* "#utility.yul":536:537 */ + 0x00 + /* "#utility.yul":529:541 */ + revert + /* "#utility.yul":482:543 */ + tag_26: + /* "#utility.yul":431:549 */ + pop + jump // out + /* "#utility.yul":555:690 */ + tag_18: + /* "#utility.yul":599:604 */ + 0x00 + /* "#utility.yul":637:643 */ + dup2 + /* "#utility.yul":624:644 */ + calldataload + /* "#utility.yul":615:644 */ + swap1 + pop + /* "#utility.yul":653:684 */ + tag_28 + /* "#utility.yul":678:683 */ + dup2 + /* "#utility.yul":653:684 */ + tag_17 + jump // in + tag_28: + /* "#utility.yul":555:690 */ + swap3 + swap2 + pop + pop + jump // out + /* "#utility.yul":696:1021 */ + tag_9: + /* "#utility.yul":753:759 */ + 0x00 + /* "#utility.yul":802:804 */ + 0x20 + /* "#utility.yul":790:799 */ + dup3 + /* "#utility.yul":781:788 */ + dup5 + /* "#utility.yul":777:800 */ + sub + /* "#utility.yul":773:805 */ + slt + /* "#utility.yul":770:889 */ + iszero + tag_30 + jumpi + /* "#utility.yul":808:887 */ + tag_31 + tag_14 + jump // in + tag_31: + /* "#utility.yul":770:889 */ + tag_30: + /* "#utility.yul":928:929 */ + 0x00 + /* "#utility.yul":953:1004 */ + tag_32 + /* "#utility.yul":996:1003 */ + dup5 + /* "#utility.yul":987:993 */ + dup3 + /* "#utility.yul":976:985 */ + dup6 + /* "#utility.yul":972:994 */ + add + /* "#utility.yul":953:1004 */ + tag_18 + jump // in + tag_32: + /* "#utility.yul":943:1004 */ + swap2 + pop + /* "#utility.yul":899:1014 */ + pop + /* "#utility.yul":696:1021 */ + swap3 + swap2 + pop + pop + jump // out + + auxdata: auxdata: +} +} + diff --git a/test/cmdlineTests/shielded_delete_sbytes32/args b/test/cmdlineTests/shielded_delete_sbytes32/args new file mode 100644 index 000000000..63727f0d9 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_sbytes32/args @@ -0,0 +1 @@ +--asm diff --git a/test/cmdlineTests/shielded_delete_sbytes32/input.sol b/test/cmdlineTests/shielded_delete_sbytes32/input.sol new file mode 100644 index 000000000..f01d6f549 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_sbytes32/input.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + sbytes32 private sbs; + + function set(sbytes32 v) external { + sbs = v; + } + + function clr() external { + delete sbs; + } +} diff --git a/test/cmdlineTests/shielded_delete_sbytes32/output b/test/cmdlineTests/shielded_delete_sbytes32/output new file mode 100644 index 000000000..57f241425 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_sbytes32/output @@ -0,0 +1,232 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= test/cmdlineTests/shielded_delete_sbytes32/input.sol:C ======= +EVM assembly: + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":60:221 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":60:221 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x0ef1346a + eq + tag_3 + jumpi + dup1 + 0xf3d2b41d + eq + tag_4 + jumpi + tag_2: + revert(0x00, 0x00) + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":168:219 function clr() external {... */ + tag_3: + tag_5 + tag_6 + jump // in + tag_5: + stop + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":104:162 function set(sbytes32 v) external {... */ + tag_4: + tag_7 + 0x04 + dup1 + calldatasize + sub + dup2 + add + swap1 + tag_8 + swap2 + swap1 + tag_9 + jump // in + tag_8: + tag_10 + jump // in + tag_7: + stop + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":168:219 function clr() external {... */ + tag_6: + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":209:212 sbs */ + 0x00 + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":202:212 delete sbs */ + 0x00 + swap1 + cstore + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":168:219 function clr() external {... */ + jump // out + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":104:162 function set(sbytes32 v) external {... */ + tag_10: + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":154:155 v */ + dup1 + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":148:151 sbs */ + 0x00 + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":148:155 sbs = v */ + dup2 + swap1 + cstore + pop + /* "test/cmdlineTests/shielded_delete_sbytes32/input.sol":104:162 function set(sbytes32 v) external {... */ + pop + jump // out + /* "#utility.yul":88:205 */ + tag_14: + /* "#utility.yul":197:198 */ + 0x00 + /* "#utility.yul":194:195 */ + 0x00 + /* "#utility.yul":187:199 */ + revert + /* "#utility.yul":334:412 */ + tag_16: + /* "#utility.yul":372:379 */ + 0x00 + /* "#utility.yul":401:406 */ + dup2 + /* "#utility.yul":390:406 */ + swap1 + pop + /* "#utility.yul":334:412 */ + swap2 + swap1 + pop + jump // out + /* "#utility.yul":418:542 */ + tag_17: + /* "#utility.yul":492:517 */ + tag_25 + /* "#utility.yul":511:516 */ + dup2 + /* "#utility.yul":492:517 */ + tag_16 + jump // in + tag_25: + /* "#utility.yul":485:490 */ + dup2 + /* "#utility.yul":482:518 */ + eq + /* "#utility.yul":472:536 */ + tag_26 + jumpi + /* "#utility.yul":532:533 */ + 0x00 + /* "#utility.yul":529:530 */ + 0x00 + /* "#utility.yul":522:534 */ + revert + /* "#utility.yul":472:536 */ + tag_26: + /* "#utility.yul":418:542 */ + pop + jump // out + /* "#utility.yul":548:689 */ + tag_18: + /* "#utility.yul":595:600 */ + 0x00 + /* "#utility.yul":633:639 */ + dup2 + /* "#utility.yul":620:640 */ + calldataload + /* "#utility.yul":611:640 */ + swap1 + pop + /* "#utility.yul":649:683 */ + tag_28 + /* "#utility.yul":677:682 */ + dup2 + /* "#utility.yul":649:683 */ + tag_17 + jump // in + tag_28: + /* "#utility.yul":548:689 */ + swap3 + swap2 + pop + pop + jump // out + /* "#utility.yul":695:1026 */ + tag_9: + /* "#utility.yul":755:761 */ + 0x00 + /* "#utility.yul":804:806 */ + 0x20 + /* "#utility.yul":792:801 */ + dup3 + /* "#utility.yul":783:790 */ + dup5 + /* "#utility.yul":779:802 */ + sub + /* "#utility.yul":775:807 */ + slt + /* "#utility.yul":772:891 */ + iszero + tag_30 + jumpi + /* "#utility.yul":810:889 */ + tag_31 + tag_14 + jump // in + tag_31: + /* "#utility.yul":772:891 */ + tag_30: + /* "#utility.yul":930:931 */ + 0x00 + /* "#utility.yul":955:1009 */ + tag_32 + /* "#utility.yul":1001:1008 */ + dup5 + /* "#utility.yul":992:998 */ + dup3 + /* "#utility.yul":981:990 */ + dup6 + /* "#utility.yul":977:999 */ + add + /* "#utility.yul":955:1009 */ + tag_18 + jump // in + tag_32: + /* "#utility.yul":945:1009 */ + swap2 + pop + /* "#utility.yul":901:1019 */ + pop + /* "#utility.yul":695:1026 */ + swap3 + swap2 + pop + pop + jump // out + + auxdata: auxdata: +} +} + diff --git a/test/cmdlineTests/shielded_delete_sint256/args b/test/cmdlineTests/shielded_delete_sint256/args new file mode 100644 index 000000000..63727f0d9 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_sint256/args @@ -0,0 +1 @@ +--asm diff --git a/test/cmdlineTests/shielded_delete_sint256/input.sol b/test/cmdlineTests/shielded_delete_sint256/input.sol new file mode 100644 index 000000000..30bb090d0 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_sint256/input.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + sint256 private si; + + function set(sint256 v) external { + si = v; + } + + function clr() external { + delete si; + } +} diff --git a/test/cmdlineTests/shielded_delete_sint256/output b/test/cmdlineTests/shielded_delete_sint256/output new file mode 100644 index 000000000..aa2838717 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_sint256/output @@ -0,0 +1,232 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= test/cmdlineTests/shielded_delete_sint256/input.sol:C ======= +EVM assembly: + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":60:216 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":60:216 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x0ef1346a + eq + tag_3 + jumpi + dup1 + 0xf9e5def4 + eq + tag_4 + jumpi + tag_2: + revert(0x00, 0x00) + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":164:214 function clr() external {... */ + tag_3: + tag_5 + tag_6 + jump // in + tag_5: + stop + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":102:158 function set(sint256 v) external {... */ + tag_4: + tag_7 + 0x04 + dup1 + calldatasize + sub + dup2 + add + swap1 + tag_8 + swap2 + swap1 + tag_9 + jump // in + tag_8: + tag_10 + jump // in + tag_7: + stop + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":164:214 function clr() external {... */ + tag_6: + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":205:207 si */ + 0x00 + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":198:207 delete si */ + 0x00 + swap1 + cstore + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":164:214 function clr() external {... */ + jump // out + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":102:158 function set(sint256 v) external {... */ + tag_10: + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":150:151 v */ + dup1 + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":145:147 si */ + 0x00 + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":145:151 si = v */ + dup2 + swap1 + cstore + pop + /* "test/cmdlineTests/shielded_delete_sint256/input.sol":102:158 function set(sint256 v) external {... */ + pop + jump // out + /* "#utility.yul":88:205 */ + tag_14: + /* "#utility.yul":197:198 */ + 0x00 + /* "#utility.yul":194:195 */ + 0x00 + /* "#utility.yul":187:199 */ + revert + /* "#utility.yul":334:411 */ + tag_16: + /* "#utility.yul":371:378 */ + 0x00 + /* "#utility.yul":400:405 */ + dup2 + /* "#utility.yul":389:405 */ + swap1 + pop + /* "#utility.yul":334:411 */ + swap2 + swap1 + pop + jump // out + /* "#utility.yul":417:539 */ + tag_17: + /* "#utility.yul":490:514 */ + tag_25 + /* "#utility.yul":508:513 */ + dup2 + /* "#utility.yul":490:514 */ + tag_16 + jump // in + tag_25: + /* "#utility.yul":483:488 */ + dup2 + /* "#utility.yul":480:515 */ + eq + /* "#utility.yul":470:533 */ + tag_26 + jumpi + /* "#utility.yul":529:530 */ + 0x00 + /* "#utility.yul":526:527 */ + 0x00 + /* "#utility.yul":519:531 */ + revert + /* "#utility.yul":470:533 */ + tag_26: + /* "#utility.yul":417:539 */ + pop + jump // out + /* "#utility.yul":545:684 */ + tag_18: + /* "#utility.yul":591:596 */ + 0x00 + /* "#utility.yul":629:635 */ + dup2 + /* "#utility.yul":616:636 */ + calldataload + /* "#utility.yul":607:636 */ + swap1 + pop + /* "#utility.yul":645:678 */ + tag_28 + /* "#utility.yul":672:677 */ + dup2 + /* "#utility.yul":645:678 */ + tag_17 + jump // in + tag_28: + /* "#utility.yul":545:684 */ + swap3 + swap2 + pop + pop + jump // out + /* "#utility.yul":690:1019 */ + tag_9: + /* "#utility.yul":749:755 */ + 0x00 + /* "#utility.yul":798:800 */ + 0x20 + /* "#utility.yul":786:795 */ + dup3 + /* "#utility.yul":777:784 */ + dup5 + /* "#utility.yul":773:796 */ + sub + /* "#utility.yul":769:801 */ + slt + /* "#utility.yul":766:885 */ + iszero + tag_30 + jumpi + /* "#utility.yul":804:883 */ + tag_31 + tag_14 + jump // in + tag_31: + /* "#utility.yul":766:885 */ + tag_30: + /* "#utility.yul":924:925 */ + 0x00 + /* "#utility.yul":949:1002 */ + tag_32 + /* "#utility.yul":994:1001 */ + dup5 + /* "#utility.yul":985:991 */ + dup3 + /* "#utility.yul":974:983 */ + dup6 + /* "#utility.yul":970:992 */ + add + /* "#utility.yul":949:1002 */ + tag_18 + jump // in + tag_32: + /* "#utility.yul":939:1002 */ + swap2 + pop + /* "#utility.yul":895:1012 */ + pop + /* "#utility.yul":690:1019 */ + swap3 + swap2 + pop + pop + jump // out + + auxdata: +} +} + diff --git a/test/cmdlineTests/shielded_delete_suint256/args b/test/cmdlineTests/shielded_delete_suint256/args new file mode 100644 index 000000000..63727f0d9 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_suint256/args @@ -0,0 +1 @@ +--asm diff --git a/test/cmdlineTests/shielded_delete_suint256/input.sol b/test/cmdlineTests/shielded_delete_suint256/input.sol new file mode 100644 index 000000000..6112dd26e --- /dev/null +++ b/test/cmdlineTests/shielded_delete_suint256/input.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + suint256 private su; + + function set(suint256 v) external { + su = v; + } + + function clr() external { + delete su; + } +} diff --git a/test/cmdlineTests/shielded_delete_suint256/output b/test/cmdlineTests/shielded_delete_suint256/output new file mode 100644 index 000000000..9ad93dd19 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_suint256/output @@ -0,0 +1,232 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= input.sol:C ======= +EVM assembly: + /* "input.sol":60:218 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "input.sol":60:218 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x0ef1346a + eq + tag_3 + jumpi + dup1 + 0xa00dddbd + eq + tag_4 + jumpi + tag_2: + revert(0x00, 0x00) + /* "input.sol":166:216 function clr() external {... */ + tag_3: + tag_5 + tag_6 + jump // in + tag_5: + stop + /* "input.sol":103:160 function set(suint256 v) external {... */ + tag_4: + tag_7 + 0x04 + dup1 + calldatasize + sub + dup2 + add + swap1 + tag_8 + swap2 + swap1 + tag_9 + jump // in + tag_8: + tag_10 + jump // in + tag_7: + stop + /* "input.sol":166:216 function clr() external {... */ + tag_6: + /* "input.sol":207:209 su */ + 0x00 + /* "input.sol":200:209 delete su */ + 0x00 + swap1 + cstore + /* "input.sol":166:216 function clr() external {... */ + jump // out + /* "input.sol":103:160 function set(suint256 v) external {... */ + tag_10: + /* "input.sol":152:153 v */ + dup1 + /* "input.sol":147:149 su */ + 0x00 + /* "input.sol":147:153 su = v */ + dup2 + swap1 + cstore + pop + /* "input.sol":103:160 function set(suint256 v) external {... */ + pop + jump // out + /* "#utility.yul":88:205 */ + tag_14: + /* "#utility.yul":197:198 */ + 0x00 + /* "#utility.yul":194:195 */ + 0x00 + /* "#utility.yul":187:199 */ + revert + /* "#utility.yul":334:412 */ + tag_16: + /* "#utility.yul":372:379 */ + 0x00 + /* "#utility.yul":401:406 */ + dup2 + /* "#utility.yul":390:406 */ + swap1 + pop + /* "#utility.yul":334:412 */ + swap2 + swap1 + pop + jump // out + /* "#utility.yul":418:542 */ + tag_17: + /* "#utility.yul":492:517 */ + tag_25 + /* "#utility.yul":511:516 */ + dup2 + /* "#utility.yul":492:517 */ + tag_16 + jump // in + tag_25: + /* "#utility.yul":485:490 */ + dup2 + /* "#utility.yul":482:518 */ + eq + /* "#utility.yul":472:536 */ + tag_26 + jumpi + /* "#utility.yul":532:533 */ + 0x00 + /* "#utility.yul":529:530 */ + 0x00 + /* "#utility.yul":522:534 */ + revert + /* "#utility.yul":472:536 */ + tag_26: + /* "#utility.yul":418:542 */ + pop + jump // out + /* "#utility.yul":548:689 */ + tag_18: + /* "#utility.yul":595:600 */ + 0x00 + /* "#utility.yul":633:639 */ + dup2 + /* "#utility.yul":620:640 */ + calldataload + /* "#utility.yul":611:640 */ + swap1 + pop + /* "#utility.yul":649:683 */ + tag_28 + /* "#utility.yul":677:682 */ + dup2 + /* "#utility.yul":649:683 */ + tag_17 + jump // in + tag_28: + /* "#utility.yul":548:689 */ + swap3 + swap2 + pop + pop + jump // out + /* "#utility.yul":695:1026 */ + tag_9: + /* "#utility.yul":755:761 */ + 0x00 + /* "#utility.yul":804:806 */ + 0x20 + /* "#utility.yul":792:801 */ + dup3 + /* "#utility.yul":783:790 */ + dup5 + /* "#utility.yul":779:802 */ + sub + /* "#utility.yul":775:807 */ + slt + /* "#utility.yul":772:891 */ + iszero + tag_30 + jumpi + /* "#utility.yul":810:889 */ + tag_31 + tag_14 + jump // in + tag_31: + /* "#utility.yul":772:891 */ + tag_30: + /* "#utility.yul":930:931 */ + 0x00 + /* "#utility.yul":955:1009 */ + tag_32 + /* "#utility.yul":1001:1008 */ + dup5 + /* "#utility.yul":992:998 */ + dup3 + /* "#utility.yul":981:990 */ + dup6 + /* "#utility.yul":977:999 */ + add + /* "#utility.yul":955:1009 */ + tag_18 + jump // in + tag_32: + /* "#utility.yul":945:1009 */ + swap2 + pop + /* "#utility.yul":901:1019 */ + pop + /* "#utility.yul":695:1026 */ + swap3 + swap2 + pop + pop + jump // out + + auxdata: auxdata: +} +} + From 12b94de99eea2e5f209d3e954395a8455543353c Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 20 Jan 2026 19:42:08 -0500 Subject: [PATCH 08/73] fix: `saddress[]` should not be implicitly convertible to `address[]` (#118) --- libsolidity/ast/Types.cpp | 2 +- ...ush_address_literal_without_conversion.sol | 10 ++++++ ..._array_push_address_without_conversion.sol | 11 +++++++ ...y_to_address_array_calldata_conversion.sol | 10 ++++++ ..._array_to_address_array_calldata_param.sol | 13 ++++++++ ..._array_to_address_array_event_emission.sol | 10 ++++++ ...s_array_to_address_array_external_call.sol | 33 +++++++++++++++++++ ...ay_to_address_array_function_parameter.sol | 27 +++++++++++++++ ...ray_to_address_array_memory_conversion.sol | 12 +++++++ ...saddress_array_to_address_array_nested.sol | 33 +++++++++++++++++++ ...ess_array_to_address_array_return_type.sol | 31 +++++++++++++++++ ...ay_to_address_array_storage_conversion.sol | 14 ++++++++ 12 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_push_address_literal_without_conversion.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_push_address_without_conversion.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_calldata_conversion.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_calldata_param.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_event_emission.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_external_call.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_function_parameter.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_memory_conversion.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_nested.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_return_type.sol create mode 100644 test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_storage_conversion.sol diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index d26f38188..fe28de802 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -543,7 +543,7 @@ TypeResult AddressType::binaryOperatorResult(Token _operator, Type const* _other bool AddressType::operator==(Type const& _other) const { - if (_other.category() != Category::Address && _other.category() != Category::ShieldedAddress) + if (_other.category() != category()) return false; AddressType const& other = dynamic_cast(_other); return other.m_stateMutability == m_stateMutability; diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_push_address_literal_without_conversion.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_push_address_literal_without_conversion.sol new file mode 100644 index 000000000..41cc06e95 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_push_address_literal_without_conversion.sol @@ -0,0 +1,10 @@ +contract C { + saddress[] sa; + + function pushAddressLiteral() external { + // Should require explicit conversion + sa.push(address(0x456)); + } +} +// ---- +// TypeError 4254: (132-139): Cannot push a non-shielded type to a shielded array diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_push_address_without_conversion.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_push_address_without_conversion.sol new file mode 100644 index 000000000..9be3b2d13 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_push_address_without_conversion.sol @@ -0,0 +1,11 @@ +contract C { + saddress[] sa; + + function pushAddress() external { + address addr = address(0x123); + // Should require explicit conversion + sa.push(addr); + } +} +// ---- +// TypeError 4254: (164-171): Cannot push a non-shielded type to a shielded array diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_calldata_conversion.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_calldata_conversion.sol new file mode 100644 index 000000000..2b4789c1e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_calldata_conversion.sol @@ -0,0 +1,10 @@ +contract C { + function testCalldataToMemory(saddress[] calldata sa, address[] calldata a) external pure { + // Assignment from calldata to memory with wrong types + address[] memory a_mem = sa; + saddress[] memory sa_mem = a; + } +} +// ---- +// TypeError 9574: (180-207): Type saddress[] calldata is not implicitly convertible to expected type address[] memory. +// TypeError 9574: (217-245): Type address[] calldata is not implicitly convertible to expected type saddress[] memory. diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_calldata_param.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_calldata_param.sol new file mode 100644 index 000000000..cfbc1d6f2 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_calldata_param.sol @@ -0,0 +1,13 @@ +contract C { + function takesAddressArrayCalldata(address[] calldata arr) internal pure {} + function takesSaddressArrayCalldata(saddress[] calldata arr) internal pure {} + + function testCalldataParam(saddress[] calldata sa, address[] calldata a) external pure { + // Both directions should fail with calldata + takesAddressArrayCalldata(sa); + takesSaddressArrayCalldata(a); + } +} +// ---- +// TypeError 9553: (356-358): Invalid type for argument in function call. Invalid implicit conversion from saddress[] calldata to address[] calldata requested. +// TypeError 9553: (396-397): Invalid type for argument in function call. Invalid implicit conversion from address[] calldata to saddress[] calldata requested. diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_event_emission.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_event_emission.sol new file mode 100644 index 000000000..d8b05c98c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_event_emission.sol @@ -0,0 +1,10 @@ +contract C { + saddress[] s; + event E(address[]); + + function emit_shielded() external { + emit E(s); + } +} +// ---- +// TypeError 9553: (111-112): Invalid type for argument in function call. Invalid implicit conversion from saddress[] storage ref to address[] memory requested. diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_external_call.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_external_call.sol new file mode 100644 index 000000000..2334b8a6d --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_external_call.sol @@ -0,0 +1,33 @@ +contract Helper { + function takesAddressArray(address[] memory arr) external pure {} + function takesSaddressArray(saddress[] memory arr) external pure {} + function returnsAddressArray() external pure returns (address[] memory) { + return new address[](5); + } + function returnsSaddressArray() external pure returns (saddress[] memory) { + return new saddress[](5); + } +} + +contract C { + Helper helper; + + function testExternalCallParam() external view { + saddress[] memory sa = new saddress[](5); + address[] memory a = new address[](5); + // Both directions should fail + helper.takesAddressArray(sa); + helper.takesSaddressArray(a); + } + + function testExternalCallReturnAssignment() external view { + // Assignment from external call with wrong return type + saddress[] memory sa = helper.returnsAddressArray(); + address[] memory a = helper.returnsSaddressArray(); + } +} +// ---- +// TypeError 9553: (655-657): Invalid type for argument in function call. Invalid implicit conversion from saddress[] memory to address[] memory requested. +// TypeError 9553: (694-695): Invalid type for argument in function call. Invalid implicit conversion from address[] memory to saddress[] memory requested. +// TypeError 9574: (841-892): Type address[] memory is not implicitly convertible to expected type saddress[] memory. +// TypeError 9574: (902-952): Type saddress[] memory is not implicitly convertible to expected type address[] memory. diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_function_parameter.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_function_parameter.sol new file mode 100644 index 000000000..f8e3d5e72 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_function_parameter.sol @@ -0,0 +1,27 @@ +contract C { + saddress[] sa; + address[] a; + + function takesAddressArray(address[] memory arr) internal pure {} + function takesSaddressArray(saddress[] memory arr) internal pure {} + + function testMemoryParam() external view { + // Passing saddress[] where address[] is expected + takesAddressArray(sa); + // Passing address[] where saddress[] is expected + takesSaddressArray(a); + } + + function testWithLocal() external pure { + saddress[] memory sa_mem = new saddress[](5); + address[] memory a_mem = new address[](5); + // Both directions should fail + takesAddressArray(sa_mem); + takesSaddressArray(a_mem); + } +} +// ---- +// TypeError 9553: (324-326): Invalid type for argument in function call. Invalid implicit conversion from saddress[] storage ref to address[] memory requested. +// TypeError 9553: (414-415): Invalid type for argument in function call. Invalid implicit conversion from address[] storage ref to saddress[] memory requested. +// TypeError 9553: (640-646): Invalid type for argument in function call. Invalid implicit conversion from saddress[] memory to address[] memory requested. +// TypeError 9553: (676-681): Invalid type for argument in function call. Invalid implicit conversion from address[] memory to saddress[] memory requested. diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_memory_conversion.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_memory_conversion.sol new file mode 100644 index 000000000..27259a5cd --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_memory_conversion.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint length) public pure { + saddress[] memory sa = new saddress[](length); + address[] memory a = new address[](length); + // Both directions should fail + a = sa; + sa = a; + } +} +// ---- +// TypeError 7407: (213-215): Type saddress[] memory is not implicitly convertible to expected type address[] memory. +// TypeError 7407: (230-231): Type address[] memory is not implicitly convertible to expected type saddress[] memory. diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_nested.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_nested.sol new file mode 100644 index 000000000..4cc0c7b69 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_nested.sol @@ -0,0 +1,33 @@ +contract C { + function testNestedArrays() external pure { + saddress[][] memory sa2d = new saddress[][](3); + address[][] memory a2d = new address[][](3); + // Both directions should fail for nested arrays + a2d = sa2d; + sa2d = a2d; + } + + function testNestedInFunction(saddress[][] memory sa2d, address[][] memory a2d) external pure { + // Assignment from parameters + address[][] memory a_local = sa2d; + saddress[][] memory sa_local = a2d; + } + + function takesNestedAddressArray(address[][] memory arr) internal pure {} + function takesNestedSaddressArray(saddress[][] memory arr) internal pure {} + + function testNestedFunctionParam() external pure { + saddress[][] memory sa2d = new saddress[][](3); + address[][] memory a2d = new address[][](3); + // Both directions should fail + takesNestedAddressArray(sa2d); + takesNestedSaddressArray(a2d); + } +} +// ---- +// TypeError 7407: (241-245): Type saddress[][] memory is not implicitly convertible to expected type address[][] memory. +// TypeError 7407: (262-265): Type address[][] memory is not implicitly convertible to expected type saddress[][] memory. +// TypeError 9574: (420-453): Type saddress[][] memory is not implicitly convertible to expected type address[][] memory. +// TypeError 9574: (463-497): Type address[][] memory is not implicitly convertible to expected type saddress[][] memory. +// TypeError 9553: (900-904): Invalid type for argument in function call. Invalid implicit conversion from saddress[][] memory to address[][] memory requested. +// TypeError 9553: (940-943): Invalid type for argument in function call. Invalid implicit conversion from address[][] memory to saddress[][] memory requested. diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_return_type.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_return_type.sol new file mode 100644 index 000000000..b9ec811aa --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_return_type.sol @@ -0,0 +1,31 @@ +contract C { + saddress[] sa; + address[] a; + + function returnsAddressArray() external view returns (address[] memory) { + // Should fail: returning saddress[] where address[] is expected + return sa; + } + + function returnsSaddressArray() external view returns (saddress[] memory) { + // Should fail: returning address[] where saddress[] is expected + return a; + } + + function returnsAddressArrayMemory() external pure returns (address[] memory) { + saddress[] memory sa_mem = new saddress[](5); + // Should fail: returning saddress[] memory where address[] memory is expected + return sa_mem; + } + + function returnsSaddressArrayMemory() external pure returns (saddress[] memory) { + address[] memory a_mem = new address[](5); + // Should fail: returning address[] memory where saddress[] memory is expected + return a_mem; + } +} +// ---- +// TypeError 6359: (216-218): Return argument type saddress[] storage ref is not implicitly convertible to expected type (type of first return variable) address[] memory. +// TypeError 6359: (395-396): Return argument type address[] storage ref is not implicitly convertible to expected type (type of first return variable) saddress[] memory. +// TypeError 6359: (645-651): Return argument type saddress[] memory is not implicitly convertible to expected type (type of first return variable) address[] memory. +// TypeError 6359: (899-904): Return argument type address[] memory is not implicitly convertible to expected type (type of first return variable) saddress[] memory. diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_storage_conversion.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_storage_conversion.sol new file mode 100644 index 000000000..f88da01fc --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_storage_conversion.sol @@ -0,0 +1,14 @@ +contract C { + saddress[] sa; + address[] a; + function f() public view { + saddress[] storage sptr = sa; + address[] storage aptr = a; + // Both directions should fail + aptr = sptr; + sptr = aptr; + } +} +// ---- +// TypeError 7407: (208-212): Type saddress[] storage pointer is not implicitly convertible to expected type address[] storage pointer. +// TypeError 7407: (229-233): Type address[] storage pointer is not implicitly convertible to expected type saddress[] storage pointer. From 44c01f2bcbfba0472153e1eb1d9cd5ffa71bc1e8 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Wed, 21 Jan 2026 11:40:38 -0500 Subject: [PATCH 09/73] fix(tstore): transient s-types should use tstore/tload (#124) --- libsolidity/codegen/LValue.cpp | 6 +- libsolidity/codegen/LValue.h | 4 + .../args | 1 + .../input.sol | 12 +++ .../output | 96 +++++++++++++++++++ .../args | 1 + .../input.sol | 12 +++ .../output | 84 ++++++++++++++++ .../args | 1 + .../input.sol | 12 +++ .../output | 84 ++++++++++++++++ .../transient_shielded_different_types.sol | 30 ++++++ .../transient_shielded_state_variable.sol | 16 ++++ 13 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 test/cmdlineTests/evmasm_transient_storage_delete_shielded/args create mode 100644 test/cmdlineTests/evmasm_transient_storage_delete_shielded/input.sol create mode 100644 test/cmdlineTests/evmasm_transient_storage_delete_shielded/output create mode 100644 test/cmdlineTests/evmasm_transient_storage_shielded_sint256/args create mode 100644 test/cmdlineTests/evmasm_transient_storage_shielded_sint256/input.sol create mode 100644 test/cmdlineTests/evmasm_transient_storage_shielded_sint256/output create mode 100644 test/cmdlineTests/evmasm_transient_storage_shielded_suint256/args create mode 100644 test/cmdlineTests/evmasm_transient_storage_shielded_suint256/input.sol create mode 100644 test/cmdlineTests/evmasm_transient_storage_shielded_suint256/output create mode 100644 test/libsolidity/semanticTests/variables/transient_shielded_different_types.sol create mode 100644 test/libsolidity/semanticTests/variables/transient_shielded_state_variable.sol diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 6a007c1e7..e189f7e5f 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -235,7 +235,7 @@ void GenericStorageItem::retrieveValue(langutil::SourceLocation con if (!_remove) CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); if (m_dataType->isShielded() && m_dataType->storageBytes() == 32) - m_context << Instruction::POP << Instruction::CLOAD; + m_context << Instruction::POP << (IsTransient ? s_loadInstruction : Instruction::CLOAD); else if (m_dataType->storageBytes() == 32) m_context << Instruction::POP << s_loadInstruction; else @@ -317,7 +317,7 @@ void GenericStorageItem::storeValue(Type const& _sourceType, langut utils.convertType(_sourceType, *m_dataType, true); m_context << Instruction::SWAP1; - m_context << Instruction::CSTORE; + m_context << (IsTransient ? s_storeInstruction : Instruction::CSTORE); } else if (m_dataType->storageBytes() == 32) { @@ -516,7 +516,7 @@ void GenericStorageItem::setToZero(langutil::SourceLocation const&, // offset should be zero. remember, shielded types have to be 32 bytes!! m_context << Instruction::POP << u256(0) - << Instruction::SWAP1 << Instruction::CSTORE; + << Instruction::SWAP1 << (IsTransient ? s_storeInstruction : Instruction::CSTORE); } else if (m_dataType->isShielded()) { diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h index 430419727..b16315bc3 100644 --- a/libsolidity/codegen/LValue.h +++ b/libsolidity/codegen/LValue.h @@ -174,6 +174,10 @@ class GenericStorageItem : public LValue bool _removeReference = true ) const override; private: + // NOTE: when using s_sload/s_storeInstruction on shielded types, + // we instead use CLOAD/CSTORE when the type is not transient. + // IMPORTANT to use similar pattern on any future usage these instructions. + // Be especially cautious about merging in upstream code that uses these static constexpr evmasm::Instruction s_storeInstruction = IsTransient ? evmasm::Instruction::TSTORE : evmasm::Instruction::SSTORE; static constexpr evmasm::Instruction s_loadInstruction = IsTransient ? evmasm::Instruction::TLOAD : evmasm::Instruction::SLOAD; }; diff --git a/test/cmdlineTests/evmasm_transient_storage_delete_shielded/args b/test/cmdlineTests/evmasm_transient_storage_delete_shielded/args new file mode 100644 index 000000000..0df1b673e --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_delete_shielded/args @@ -0,0 +1 @@ +--no-cbor-metadata --optimize --asm --debug-info none diff --git a/test/cmdlineTests/evmasm_transient_storage_delete_shielded/input.sol b/test/cmdlineTests/evmasm_transient_storage_delete_shielded/input.sol new file mode 100644 index 000000000..7ec58211c --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_delete_shielded/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + suint256 transient x; + function set(suint256 v) external { + x = v; + } + function clr() external { + delete x; + } +} diff --git a/test/cmdlineTests/evmasm_transient_storage_delete_shielded/output b/test/cmdlineTests/evmasm_transient_storage_delete_shielded/output new file mode 100644 index 000000000..baa12fb37 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_delete_shielded/output @@ -0,0 +1,96 @@ + +======= input.sol:C ======= +EVM assembly: + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x0ef1346a + eq + tag_3 + jumpi + dup1 + 0xa00dddbd + eq + tag_4 + jumpi + tag_2: + revert(0x00, 0x00) + tag_3: + tag_5 + tag_6 + jump // in + tag_5: + stop + tag_4: + tag_5 + tag_8 + calldatasize + 0x04 + tag_9 + jump // in + tag_8: + tag_10 + jump // in + tag_6: + 0x00 + dup1 + tstore + jump // out + tag_10: + dup1 + dup1 + 0x00 + tstore + pop + pop + jump // out + tag_9: + 0x00 + 0x20 + dup3 + dup5 + sub + slt + iszero + tag_15 + jumpi + 0x00 + 0x00 + revert + tag_15: + pop + calldataload + swap2 + swap1 + pop + jump // out +} + diff --git a/test/cmdlineTests/evmasm_transient_storage_shielded_sint256/args b/test/cmdlineTests/evmasm_transient_storage_shielded_sint256/args new file mode 100644 index 000000000..0df1b673e --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_shielded_sint256/args @@ -0,0 +1 @@ +--no-cbor-metadata --optimize --asm --debug-info none diff --git a/test/cmdlineTests/evmasm_transient_storage_shielded_sint256/input.sol b/test/cmdlineTests/evmasm_transient_storage_shielded_sint256/input.sol new file mode 100644 index 000000000..78f227d8a --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_shielded_sint256/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + sint256 transient x; + function set(sint256 v) external { + x = v; + } + function get() external view { + x; + } +} diff --git a/test/cmdlineTests/evmasm_transient_storage_shielded_sint256/output b/test/cmdlineTests/evmasm_transient_storage_shielded_sint256/output new file mode 100644 index 000000000..1023c749a --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_shielded_sint256/output @@ -0,0 +1,84 @@ + +======= input.sol:C ======= +EVM assembly: + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x6d4ce63c + eq + tag_3 + jumpi + dup1 + 0xf9e5def4 + eq + tag_4 + jumpi + tag_2: + revert(0x00, 0x00) + tag_3: + stop + tag_4: + tag_3 + tag_8 + calldatasize + 0x04 + tag_9 + jump // in + tag_8: + dup1 + dup1 + 0x00 + tstore + pop + pop + jump // out + tag_9: + 0x00 + 0x20 + dup3 + dup5 + sub + slt + iszero + tag_15 + jumpi + 0x00 + 0x00 + revert + tag_15: + pop + calldataload + swap2 + swap1 + pop + jump // out +} + diff --git a/test/cmdlineTests/evmasm_transient_storage_shielded_suint256/args b/test/cmdlineTests/evmasm_transient_storage_shielded_suint256/args new file mode 100644 index 000000000..0df1b673e --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_shielded_suint256/args @@ -0,0 +1 @@ +--no-cbor-metadata --optimize --asm --debug-info none diff --git a/test/cmdlineTests/evmasm_transient_storage_shielded_suint256/input.sol b/test/cmdlineTests/evmasm_transient_storage_shielded_suint256/input.sol new file mode 100644 index 000000000..f72159809 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_shielded_suint256/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + suint256 transient x; + function set(suint256 v) external { + x = v; + } + function get() external view { + x; + } +} diff --git a/test/cmdlineTests/evmasm_transient_storage_shielded_suint256/output b/test/cmdlineTests/evmasm_transient_storage_shielded_suint256/output new file mode 100644 index 000000000..9472ec8dc --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_shielded_suint256/output @@ -0,0 +1,84 @@ + +======= input.sol:C ======= +EVM assembly: + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x6d4ce63c + eq + tag_3 + jumpi + dup1 + 0xa00dddbd + eq + tag_4 + jumpi + tag_2: + revert(0x00, 0x00) + tag_3: + stop + tag_4: + tag_3 + tag_8 + calldatasize + 0x04 + tag_9 + jump // in + tag_8: + dup1 + dup1 + 0x00 + tstore + pop + pop + jump // out + tag_9: + 0x00 + 0x20 + dup3 + dup5 + sub + slt + iszero + tag_15 + jumpi + 0x00 + 0x00 + revert + tag_15: + pop + calldataload + swap2 + swap1 + pop + jump // out +} + diff --git a/test/libsolidity/semanticTests/variables/transient_shielded_different_types.sol b/test/libsolidity/semanticTests/variables/transient_shielded_different_types.sol new file mode 100644 index 000000000..ec22c61f8 --- /dev/null +++ b/test/libsolidity/semanticTests/variables/transient_shielded_different_types.sol @@ -0,0 +1,30 @@ +contract C { + suint256 transient su; + sint256 transient si; + saddress transient sa; + sbool transient sb; + + function setAndCheckSu(uint256 v) public returns (uint256) { + su = suint256(v); + return uint256(su); + } + function setAndCheckSi(int256 v) public returns (int256) { + si = sint256(v); + return int256(si); + } + function setAndCheckSa(address v) public returns (address) { + sa = saddress(v); + return address(sa); + } + function setAndCheckSb(bool v) public returns (bool) { + sb = sbool(v); + return bool(sb); + } +} +// ==== +// EVMVersion: >=mercury +// ---- +// setAndCheckSu(uint256): 123 -> 123 +// setAndCheckSi(int256): -456 -> -456 +// setAndCheckSa(address): 0x1234567890123456789012345678901234567890 -> 0x1234567890123456789012345678901234567890 +// setAndCheckSb(bool): true -> true diff --git a/test/libsolidity/semanticTests/variables/transient_shielded_state_variable.sol b/test/libsolidity/semanticTests/variables/transient_shielded_state_variable.sol new file mode 100644 index 000000000..c50d3618a --- /dev/null +++ b/test/libsolidity/semanticTests/variables/transient_shielded_state_variable.sol @@ -0,0 +1,16 @@ +contract C { + suint256 transient x; + + function setAndCheck(uint256 v) public returns (uint256) { + x = suint256(v); + return uint256(x); + } + function checkX() public view returns (uint256) { + return uint256(x); + } +} +// ==== +// EVMVersion: >=mercury +// ---- +// setAndCheck(uint256): 42 -> 42 +// checkX() -> 0 From 8cea3d16be66632f019b50e23c607f3316556299 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Wed, 21 Jan 2026 13:36:54 -0500 Subject: [PATCH 10/73] fix: --via-ir pipeline should support ShieldedFixedBytes (#125) --- libsolidity/codegen/YulUtilFunctions.cpp | 35 +++ .../shielded_sbytes16_via_ir/args | 1 + .../shielded_sbytes16_via_ir/input.sol | 9 + .../shielded_sbytes16_via_ir/output | 231 ++++++++++++++++++ .../cmdlineTests/shielded_sbytes1_via_ir/args | 1 + .../shielded_sbytes1_via_ir/input.sol | 9 + .../shielded_sbytes1_via_ir/output | 231 ++++++++++++++++++ .../shielded_sbytes32_via_ir/args | 1 + .../shielded_sbytes32_via_ir/input.sol | 9 + .../shielded_sbytes32_via_ir/output | 224 +++++++++++++++++ .../cmdlineTests/shielded_sbytes8_via_ir/args | 1 + .../shielded_sbytes8_via_ir/input.sol | 9 + .../shielded_sbytes8_via_ir/output | 231 ++++++++++++++++++ 13 files changed, 992 insertions(+) create mode 100644 test/cmdlineTests/shielded_sbytes16_via_ir/args create mode 100644 test/cmdlineTests/shielded_sbytes16_via_ir/input.sol create mode 100644 test/cmdlineTests/shielded_sbytes16_via_ir/output create mode 100644 test/cmdlineTests/shielded_sbytes1_via_ir/args create mode 100644 test/cmdlineTests/shielded_sbytes1_via_ir/input.sol create mode 100644 test/cmdlineTests/shielded_sbytes1_via_ir/output create mode 100644 test/cmdlineTests/shielded_sbytes32_via_ir/args create mode 100644 test/cmdlineTests/shielded_sbytes32_via_ir/input.sol create mode 100644 test/cmdlineTests/shielded_sbytes32_via_ir/output create mode 100644 test/cmdlineTests/shielded_sbytes8_via_ir/args create mode 100644 test/cmdlineTests/shielded_sbytes8_via_ir/input.sol create mode 100644 test/cmdlineTests/shielded_sbytes8_via_ir/output diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 60cbd0661..c4252a5ce 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -3652,6 +3652,41 @@ std::string YulUtilFunctions::conversionFunction(Type const& _from, Type const& } break; } + case Type::Category::ShieldedFixedBytes: + { + ShieldedFixedBytesType const& from = dynamic_cast(_from); + if (toCategory == Type::Category::Integer || toCategory == Type::Category::ShieldedInteger) + body = + Whiskers("converted := ((value))") + ("shift", shiftRightFunction(256 - from.numBytes() * 8)) + ("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to)) + .render(); + else if (toCategory == Type::Category::Address || toCategory == Type::Category::ShieldedAddress) + body = + Whiskers("converted := (value)") + ("convert", conversionFunction(_from, IntegerType(160))) + .render(); + else if (toCategory == Type::Category::FixedBytes) + { + // ShieldedFixedBytes to FixedBytes (same size) + FixedBytesType const& to = dynamic_cast(_to); + solAssert(from.numBytes() == to.numBytes(), "Invalid conversion between sbytes and bytes of different sizes."); + body = + Whiskers("converted := (value)") + ("clean", cleanupFunction(to)) + .render(); + } + else + { + solAssert(toCategory == Type::Category::ShieldedFixedBytes, "Invalid type conversion requested."); + ShieldedFixedBytesType const& to = dynamic_cast(_to); + body = + Whiskers("converted := (value)") + ("clean", cleanupFunction((to.numBytes() <= from.numBytes()) ? to : from)) + .render(); + } + break; + } case Type::Category::Function: { solAssert(false, "Conversion should not be called for function types."); diff --git a/test/cmdlineTests/shielded_sbytes16_via_ir/args b/test/cmdlineTests/shielded_sbytes16_via_ir/args new file mode 100644 index 000000000..9b1d485a3 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes16_via_ir/args @@ -0,0 +1 @@ +--asm --via-ir diff --git a/test/cmdlineTests/shielded_sbytes16_via_ir/input.sol b/test/cmdlineTests/shielded_sbytes16_via_ir/input.sol new file mode 100644 index 000000000..9c2f1ba91 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes16_via_ir/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + sbytes16 data; + function test() external { + data = sbytes16(0); + } +} diff --git a/test/cmdlineTests/shielded_sbytes16_via_ir/output b/test/cmdlineTests/shielded_sbytes16_via_ir/output new file mode 100644 index 000000000..7fe68f6fb --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes16_via_ir/output @@ -0,0 +1,231 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= shielded_sbytes16_via_ir/input.sol:C ======= +EVM assembly: + /* "shielded_sbytes16_via_ir/input.sol":60:158 contract C {... */ + mstore(0x40, 0x80) + jumpi(tag_3, callvalue) + tag_5 + tag_1 + jump // in +tag_5: + dataSize(sub_0) + dataOffset(sub_0) + dup3 + codecopy + dataSize(sub_0) + swap1 + return +tag_3: + tag_2 + jump // in +tag_1: + mload(0x40) + swap1 + jump // out +tag_2: + 0x00 + dup1 + revert +stop + +sub_0: assembly { + /* "shielded_sbytes16_via_ir/input.sol":60:158 contract C {... */ + mstore(0x40, 0x80) + jumpi(tag_20, iszero(lt(calldatasize, 0x04))) + tag_21: + tag_8 + jump // in + tag_20: + tag_22 + calldataload(0x00) + tag_1 + jump // in + tag_22: + 0xf8a8fd6d + sub + tag_21 + jumpi + tag_7 + jump // in + tag_1: + 0xe0 + shr + swap1 + jump // out + tag_2: + mload(0x40) + swap1 + jump // out + tag_3: + 0x00 + dup1 + revert + tag_4: + 0x00 + dup1 + revert + tag_5: + 0x00 + swap2 + sub + slt + tag_25 + jumpi + jump // out + tag_25: + tag_4 + jump // in + tag_6: + 0x00 + add + swap1 + jump // out + tag_7: + jumpi(tag_27, callvalue) + tag_29 + calldatasize + 0x04 + tag_5 + jump // in + tag_29: + tag_30 + tag_19 + jump // in + tag_30: + tag_31 + tag_2 + jump // in + tag_31: + dup1 + tag_32 + dup2 + tag_6 + jump // in + tag_32: + sub + swap1 + return + tag_27: + tag_3 + jump // in + tag_8: + 0x00 + dup1 + revert + tag_9: + swap1 + jump // out + tag_10: + not(0xffffffffffffffffffffffffffffffff) + and + swap1 + jump // out + tag_11: + 0x80 + shl + swap1 + jump // out + tag_12: + tag_33 + tag_34 + tag_35 + swap3 + tag_9 + jump // in + tag_34: + tag_11 + jump // in + tag_33: + tag_10 + jump // in + tag_35: + swap1 + jump // out + tag_13: + 0x00 + shl + swap1 + jump // out + tag_14: + swap1 + tag_36 + not(0x00) + swap2 + tag_13 + jump // in + tag_36: + swap2 + dup2 + not + and + swap2 + and + or + swap1 + jump // out + tag_15: + tag_37 + swap1 + tag_10 + jump // in + tag_37: + swap1 + jump // out + tag_16: + 0x00 + shr + swap1 + jump // out + tag_17: + tag_38 + swap1 + tag_16 + jump // in + tag_38: + swap1 + jump // out + tag_18: + swap1 + tag_39 + tag_40 + tag_41 + swap3 + tag_15 + jump // in + tag_40: + tag_17 + jump // in + tag_39: + dup3 + cload + tag_14 + jump // in + tag_41: + swap1 + cstore + jump // out + /* "shielded_sbytes16_via_ir/input.sol":96:156 function test() external {... */ + tag_19: + /* "shielded_sbytes16_via_ir/input.sol":131:149 data = sbytes16(0) */ + tag_42 + /* "shielded_sbytes16_via_ir/input.sol":138:149 sbytes16(0) */ + tag_43 + /* "shielded_sbytes16_via_ir/input.sol":147:148 0 */ + 0x00 + /* "shielded_sbytes16_via_ir/input.sol":138:149 sbytes16(0) */ + tag_12 + jump // in + tag_43: + /* "shielded_sbytes16_via_ir/input.sol":131:149 data = sbytes16(0) */ + 0x00 + tag_18 + jump // in + tag_42: + /* "shielded_sbytes16_via_ir/input.sol":96:156 function test() external {... */ + jump // out + + auxdata: 0xa264697066735822122037b55db07a67fb1a37064c30080d17923eabae62f0cdd6d344db02a833c23ccb64736f6c63782c302e382e33312d646576656c6f702e323032362e312e32312b636f6d6d69742e38346365363530622e6d6f64005d +} + diff --git a/test/cmdlineTests/shielded_sbytes1_via_ir/args b/test/cmdlineTests/shielded_sbytes1_via_ir/args new file mode 100644 index 000000000..9b1d485a3 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes1_via_ir/args @@ -0,0 +1 @@ +--asm --via-ir diff --git a/test/cmdlineTests/shielded_sbytes1_via_ir/input.sol b/test/cmdlineTests/shielded_sbytes1_via_ir/input.sol new file mode 100644 index 000000000..9ce8ee6b7 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes1_via_ir/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + sbytes1 data; + function test() external { + data = sbytes1(0); + } +} diff --git a/test/cmdlineTests/shielded_sbytes1_via_ir/output b/test/cmdlineTests/shielded_sbytes1_via_ir/output new file mode 100644 index 000000000..655823d28 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes1_via_ir/output @@ -0,0 +1,231 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= shielded_sbytes1_via_ir/input.sol:C ======= +EVM assembly: + /* "shielded_sbytes1_via_ir/input.sol":60:156 contract C {... */ + mstore(0x40, 0x80) + jumpi(tag_3, callvalue) + tag_5 + tag_1 + jump // in +tag_5: + dataSize(sub_0) + dataOffset(sub_0) + dup3 + codecopy + dataSize(sub_0) + swap1 + return +tag_3: + tag_2 + jump // in +tag_1: + mload(0x40) + swap1 + jump // out +tag_2: + 0x00 + dup1 + revert +stop + +sub_0: assembly { + /* "shielded_sbytes1_via_ir/input.sol":60:156 contract C {... */ + mstore(0x40, 0x80) + jumpi(tag_20, iszero(lt(calldatasize, 0x04))) + tag_21: + tag_8 + jump // in + tag_20: + tag_22 + calldataload(0x00) + tag_1 + jump // in + tag_22: + 0xf8a8fd6d + sub + tag_21 + jumpi + tag_7 + jump // in + tag_1: + 0xe0 + shr + swap1 + jump // out + tag_2: + mload(0x40) + swap1 + jump // out + tag_3: + 0x00 + dup1 + revert + tag_4: + 0x00 + dup1 + revert + tag_5: + 0x00 + swap2 + sub + slt + tag_25 + jumpi + jump // out + tag_25: + tag_4 + jump // in + tag_6: + 0x00 + add + swap1 + jump // out + tag_7: + jumpi(tag_27, callvalue) + tag_29 + calldatasize + 0x04 + tag_5 + jump // in + tag_29: + tag_30 + tag_19 + jump // in + tag_30: + tag_31 + tag_2 + jump // in + tag_31: + dup1 + tag_32 + dup2 + tag_6 + jump // in + tag_32: + sub + swap1 + return + tag_27: + tag_3 + jump // in + tag_8: + 0x00 + dup1 + revert + tag_9: + swap1 + jump // out + tag_10: + shl(0xf8, 0xff) + and + swap1 + jump // out + tag_11: + 0xf8 + shl + swap1 + jump // out + tag_12: + tag_33 + tag_34 + tag_35 + swap3 + tag_9 + jump // in + tag_34: + tag_11 + jump // in + tag_33: + tag_10 + jump // in + tag_35: + swap1 + jump // out + tag_13: + 0x00 + shl + swap1 + jump // out + tag_14: + swap1 + tag_36 + not(0x00) + swap2 + tag_13 + jump // in + tag_36: + swap2 + dup2 + not + and + swap2 + and + or + swap1 + jump // out + tag_15: + tag_37 + swap1 + tag_10 + jump // in + tag_37: + swap1 + jump // out + tag_16: + 0x00 + shr + swap1 + jump // out + tag_17: + tag_38 + swap1 + tag_16 + jump // in + tag_38: + swap1 + jump // out + tag_18: + swap1 + tag_39 + tag_40 + tag_41 + swap3 + tag_15 + jump // in + tag_40: + tag_17 + jump // in + tag_39: + dup3 + cload + tag_14 + jump // in + tag_41: + swap1 + cstore + jump // out + /* "shielded_sbytes1_via_ir/input.sol":95:154 function test() external {... */ + tag_19: + /* "shielded_sbytes1_via_ir/input.sol":130:147 data = sbytes1(0) */ + tag_42 + /* "shielded_sbytes1_via_ir/input.sol":137:147 sbytes1(0) */ + tag_43 + /* "shielded_sbytes1_via_ir/input.sol":145:146 0 */ + 0x00 + /* "shielded_sbytes1_via_ir/input.sol":137:147 sbytes1(0) */ + tag_12 + jump // in + tag_43: + /* "shielded_sbytes1_via_ir/input.sol":130:147 data = sbytes1(0) */ + 0x00 + tag_18 + jump // in + tag_42: + /* "shielded_sbytes1_via_ir/input.sol":95:154 function test() external {... */ + jump // out + + auxdata: 0xa26469706673582212202b26554c6aa2658af95ca8a84ff072edd4a43ec2a47a56722a7cd769e7f53d5364736f6c63782c302e382e33312d646576656c6f702e323032362e312e32312b636f6d6d69742e38346365363530622e6d6f64005d +} + diff --git a/test/cmdlineTests/shielded_sbytes32_via_ir/args b/test/cmdlineTests/shielded_sbytes32_via_ir/args new file mode 100644 index 000000000..9b1d485a3 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes32_via_ir/args @@ -0,0 +1 @@ +--asm --via-ir diff --git a/test/cmdlineTests/shielded_sbytes32_via_ir/input.sol b/test/cmdlineTests/shielded_sbytes32_via_ir/input.sol new file mode 100644 index 000000000..585cdd245 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes32_via_ir/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract Blah { + sbytes32 data; + function test() external { + data = sbytes32(0); + } +} diff --git a/test/cmdlineTests/shielded_sbytes32_via_ir/output b/test/cmdlineTests/shielded_sbytes32_via_ir/output new file mode 100644 index 000000000..73bc40ef4 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes32_via_ir/output @@ -0,0 +1,224 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= shielded_sbytes32_via_ir/input.sol:Blah ======= +EVM assembly: + /* "shielded_sbytes32_via_ir/input.sol":60:161 contract Blah {... */ + mstore(0x40, 0x80) + jumpi(tag_3, callvalue) + tag_5 + tag_1 + jump // in +tag_5: + dataSize(sub_0) + dataOffset(sub_0) + dup3 + codecopy + dataSize(sub_0) + swap1 + return +tag_3: + tag_2 + jump // in +tag_1: + mload(0x40) + swap1 + jump // out +tag_2: + 0x00 + dup1 + revert +stop + +sub_0: assembly { + /* "shielded_sbytes32_via_ir/input.sol":60:161 contract Blah {... */ + mstore(0x40, 0x80) + jumpi(tag_19, iszero(lt(calldatasize, 0x04))) + tag_20: + tag_8 + jump // in + tag_19: + tag_21 + calldataload(0x00) + tag_1 + jump // in + tag_21: + 0xf8a8fd6d + sub + tag_20 + jumpi + tag_7 + jump // in + tag_1: + 0xe0 + shr + swap1 + jump // out + tag_2: + mload(0x40) + swap1 + jump // out + tag_3: + 0x00 + dup1 + revert + tag_4: + 0x00 + dup1 + revert + tag_5: + 0x00 + swap2 + sub + slt + tag_24 + jumpi + jump // out + tag_24: + tag_4 + jump // in + tag_6: + 0x00 + add + swap1 + jump // out + tag_7: + jumpi(tag_26, callvalue) + tag_28 + calldatasize + 0x04 + tag_5 + jump // in + tag_28: + tag_29 + tag_18 + jump // in + tag_29: + tag_30 + tag_2 + jump // in + tag_30: + dup1 + tag_31 + dup2 + tag_6 + jump // in + tag_31: + sub + swap1 + return + tag_26: + tag_3 + jump // in + tag_8: + 0x00 + dup1 + revert + tag_9: + swap1 + jump // out + tag_10: + swap1 + jump // out + tag_11: + 0x00 + shl + swap1 + jump // out + tag_12: + tag_32 + tag_33 + tag_34 + swap3 + tag_9 + jump // in + tag_33: + tag_11 + jump // in + tag_32: + tag_10 + jump // in + tag_34: + swap1 + jump // out + tag_13: + swap1 + tag_35 + not(0x00) + swap2 + tag_11 + jump // in + tag_35: + swap2 + dup2 + not + and + swap2 + and + or + swap1 + jump // out + tag_14: + tag_36 + swap1 + tag_10 + jump // in + tag_36: + swap1 + jump // out + tag_15: + 0x00 + shr + swap1 + jump // out + tag_16: + tag_37 + swap1 + tag_15 + jump // in + tag_37: + swap1 + jump // out + tag_17: + swap1 + tag_38 + tag_39 + tag_40 + swap3 + tag_14 + jump // in + tag_39: + tag_16 + jump // in + tag_38: + dup3 + cload + tag_13 + jump // in + tag_40: + swap1 + cstore + jump // out + /* "shielded_sbytes32_via_ir/input.sol":99:159 function test() external {... */ + tag_18: + /* "shielded_sbytes32_via_ir/input.sol":134:152 data = sbytes32(0) */ + tag_41 + /* "shielded_sbytes32_via_ir/input.sol":141:152 sbytes32(0) */ + tag_42 + /* "shielded_sbytes32_via_ir/input.sol":150:151 0 */ + 0x00 + /* "shielded_sbytes32_via_ir/input.sol":141:152 sbytes32(0) */ + tag_12 + jump // in + tag_42: + /* "shielded_sbytes32_via_ir/input.sol":134:152 data = sbytes32(0) */ + 0x00 + tag_17 + jump // in + tag_41: + /* "shielded_sbytes32_via_ir/input.sol":99:159 function test() external {... */ + jump // out + + auxdata: 0xa26469706673582212207984999c96d771d415fc6fc5aa522d4585706eff88cb5b62f4075b6d9b472ae564736f6c63782c302e382e33312d646576656c6f702e323032362e312e32312b636f6d6d69742e38346365363530622e6d6f64005d +} + diff --git a/test/cmdlineTests/shielded_sbytes8_via_ir/args b/test/cmdlineTests/shielded_sbytes8_via_ir/args new file mode 100644 index 000000000..9b1d485a3 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes8_via_ir/args @@ -0,0 +1 @@ +--asm --via-ir diff --git a/test/cmdlineTests/shielded_sbytes8_via_ir/input.sol b/test/cmdlineTests/shielded_sbytes8_via_ir/input.sol new file mode 100644 index 000000000..54caeb286 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes8_via_ir/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + sbytes8 data; + function test() external { + data = sbytes8(0); + } +} diff --git a/test/cmdlineTests/shielded_sbytes8_via_ir/output b/test/cmdlineTests/shielded_sbytes8_via_ir/output new file mode 100644 index 000000000..9dafafee9 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes8_via_ir/output @@ -0,0 +1,231 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= shielded_sbytes8_via_ir/input.sol:C ======= +EVM assembly: + /* "shielded_sbytes8_via_ir/input.sol":60:156 contract C {... */ + mstore(0x40, 0x80) + jumpi(tag_3, callvalue) + tag_5 + tag_1 + jump // in +tag_5: + dataSize(sub_0) + dataOffset(sub_0) + dup3 + codecopy + dataSize(sub_0) + swap1 + return +tag_3: + tag_2 + jump // in +tag_1: + mload(0x40) + swap1 + jump // out +tag_2: + 0x00 + dup1 + revert +stop + +sub_0: assembly { + /* "shielded_sbytes8_via_ir/input.sol":60:156 contract C {... */ + mstore(0x40, 0x80) + jumpi(tag_20, iszero(lt(calldatasize, 0x04))) + tag_21: + tag_8 + jump // in + tag_20: + tag_22 + calldataload(0x00) + tag_1 + jump // in + tag_22: + 0xf8a8fd6d + sub + tag_21 + jumpi + tag_7 + jump // in + tag_1: + 0xe0 + shr + swap1 + jump // out + tag_2: + mload(0x40) + swap1 + jump // out + tag_3: + 0x00 + dup1 + revert + tag_4: + 0x00 + dup1 + revert + tag_5: + 0x00 + swap2 + sub + slt + tag_25 + jumpi + jump // out + tag_25: + tag_4 + jump // in + tag_6: + 0x00 + add + swap1 + jump // out + tag_7: + jumpi(tag_27, callvalue) + tag_29 + calldatasize + 0x04 + tag_5 + jump // in + tag_29: + tag_30 + tag_19 + jump // in + tag_30: + tag_31 + tag_2 + jump // in + tag_31: + dup1 + tag_32 + dup2 + tag_6 + jump // in + tag_32: + sub + swap1 + return + tag_27: + tag_3 + jump // in + tag_8: + 0x00 + dup1 + revert + tag_9: + swap1 + jump // out + tag_10: + shl(0xc0, 0xffffffffffffffff) + and + swap1 + jump // out + tag_11: + 0xc0 + shl + swap1 + jump // out + tag_12: + tag_33 + tag_34 + tag_35 + swap3 + tag_9 + jump // in + tag_34: + tag_11 + jump // in + tag_33: + tag_10 + jump // in + tag_35: + swap1 + jump // out + tag_13: + 0x00 + shl + swap1 + jump // out + tag_14: + swap1 + tag_36 + not(0x00) + swap2 + tag_13 + jump // in + tag_36: + swap2 + dup2 + not + and + swap2 + and + or + swap1 + jump // out + tag_15: + tag_37 + swap1 + tag_10 + jump // in + tag_37: + swap1 + jump // out + tag_16: + 0x00 + shr + swap1 + jump // out + tag_17: + tag_38 + swap1 + tag_16 + jump // in + tag_38: + swap1 + jump // out + tag_18: + swap1 + tag_39 + tag_40 + tag_41 + swap3 + tag_15 + jump // in + tag_40: + tag_17 + jump // in + tag_39: + dup3 + cload + tag_14 + jump // in + tag_41: + swap1 + cstore + jump // out + /* "shielded_sbytes8_via_ir/input.sol":95:154 function test() external {... */ + tag_19: + /* "shielded_sbytes8_via_ir/input.sol":130:147 data = sbytes8(0) */ + tag_42 + /* "shielded_sbytes8_via_ir/input.sol":137:147 sbytes8(0) */ + tag_43 + /* "shielded_sbytes8_via_ir/input.sol":145:146 0 */ + 0x00 + /* "shielded_sbytes8_via_ir/input.sol":137:147 sbytes8(0) */ + tag_12 + jump // in + tag_43: + /* "shielded_sbytes8_via_ir/input.sol":130:147 data = sbytes8(0) */ + 0x00 + tag_18 + jump // in + tag_42: + /* "shielded_sbytes8_via_ir/input.sol":95:154 function test() external {... */ + jump // out + + auxdata: 0xa264697066735822122019b1264fa2512459ee1fa9c98bdff7cce4a952bc9637da13537e7f43a55646f164736f6c63782c302e382e33312d646576656c6f702e323032362e312e32312b636f6d6d69742e38346365363530622e6d6f64005d +} + From 1ef32a128f4569d1aabcd3e58b013f0c07f1878d Mon Sep 17 00:00:00 2001 From: cdrappi Date: Thu, 22 Jan 2026 11:12:49 -0500 Subject: [PATCH 11/73] test: add failing test for shielded array deletion This test demonstrates the bug where ArrayUtils::clearArray uses hardcoded SSTORE instead of CSTORE for small shielded arrays. The test shows that deleting shielded arrays like suint256[3] or saddress[4] incorrectly generates SSTORE instructions in the unrolled loop optimization path. This will be fixed in the next commit. --- test/cmdlineTests/shielded_delete_array/args | 1 + .../shielded_delete_array/input.sol | 20 +++ .../cmdlineTests/shielded_delete_array/output | 145 ++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 test/cmdlineTests/shielded_delete_array/args create mode 100644 test/cmdlineTests/shielded_delete_array/input.sol create mode 100644 test/cmdlineTests/shielded_delete_array/output diff --git a/test/cmdlineTests/shielded_delete_array/args b/test/cmdlineTests/shielded_delete_array/args new file mode 100644 index 000000000..a73dc7c21 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_array/args @@ -0,0 +1 @@ +--optimize --asm diff --git a/test/cmdlineTests/shielded_delete_array/input.sol b/test/cmdlineTests/shielded_delete_array/input.sol new file mode 100644 index 000000000..6283d4069 --- /dev/null +++ b/test/cmdlineTests/shielded_delete_array/input.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + suint256[3] private smallArray; + suint256[10] private largeArray; + saddress[4] private saddrs; + + function clearSmall() public { + delete smallArray; + } + + function clearLarge() public { + delete largeArray; + } + + function clearAddrs() public { + delete saddrs; + } +} diff --git a/test/cmdlineTests/shielded_delete_array/output b/test/cmdlineTests/shielded_delete_array/output new file mode 100644 index 000000000..35385dfab --- /dev/null +++ b/test/cmdlineTests/shielded_delete_array/output @@ -0,0 +1,145 @@ +Warning: This is a pre-release compiler version, please do not use it in production. + + +======= input.sol:C ======= +EVM assembly: + /* "input.sol":60:382 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "input.sol":60:382 contract C {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0x131f5cd6 + eq + tag_3 + jumpi + dup1 + 0xc6ae8c0c + eq + tag_4 + jumpi + dup1 + 0xe6bb513b + eq + tag_5 + jumpi + tag_2: + revert(0x00, 0x00) + /* "input.sol":252:315 function clearLarge() public {... */ + tag_3: + tag_6 + tag_7 + jump // in + tag_6: + stop + /* "input.sol":183:246 function clearSmall() public {... */ + tag_4: + tag_6 + /* "input.sol":229:239 smallArray */ + 0x00 + dup1 + dup1 + sstore + 0x01 + dup2 + swap1 + sstore + 0x02 + sstore + /* "input.sol":252:315 function clearLarge() public {... */ + jump + /* "input.sol":321:380 function clearAddrs() public {... */ + tag_5: + tag_6 + /* "input.sol":367:373 saddrs */ + 0x00 + 0x0d + dup2 + swap1 + sstore + 0x0e + dup2 + swap1 + sstore + 0x0f + dup2 + swap1 + sstore + 0x10 + sstore + /* "input.sol":252:315 function clearLarge() public {... */ + jump + tag_7: + /* "input.sol":291:308 delete largeArray */ + tag_13 + /* "input.sol":298:308 largeArray */ + 0x03 + 0x00 + /* "input.sol":291:308 delete largeArray */ + tag_14 + jump // in + tag_13: + /* "input.sol":252:315 function clearLarge() public {... */ + jump // out + tag_14: + pop + tag_21 + swap1 + 0x0a + dup2 + add + swap1 + tag_22 + jump // in + tag_21: + pop + jump // out + tag_22: + tag_23: + dup1 + dup3 + gt + iszero + tag_24 + jumpi + 0x00 + dup2 + cstore + 0x01 + add + jump(tag_23) + tag_24: + pop + swap1 + jump // out + + auxdata: 0xa2646970667358221220c773b0f9dcaec87d4bc80d443c6d9384f9b062cae07690dfcf1a87d40fb7609264736f6c637828302e382e33312d646576656c6f702e323032362e312e32322b636f6d6d69742e38636561336431360059 +} + From 4e35d34253db9dae577b68e54e00a8f1aa8d2c38 Mon Sep 17 00:00:00 2001 From: cdrappi Date: Thu, 22 Jan 2026 11:14:18 -0500 Subject: [PATCH 12/73] fix: ArrayUtils::clearArray should use CSTORE for shielded arrays The original fix in commit 5c75476 addressed GenericStorageItem::setToZero, but ArrayUtils::clearArray had an optimization path that bypassed it. For small fixed-size arrays (storageSize <= 5), clearArray uses an unrolled loop that directly emits store instructions. This code path was using hardcoded SSTORE without checking if the base type is shielded. This meant that operations like `delete suint256[3]` would incorrectly use SSTORE instead of CSTORE, leaking private data to public storage. The fix checks if the array's base type is shielded and uses CSTORE for shielded types, SSTORE for non-shielded types. Affected code path: - ArrayUtils::clearArray lines 568-578 (unrolled loop for small arrays) Other code paths correctly use StorageItem::setToZero which handles shielded types properly after the original fix. --- libsolidity/codegen/ArrayUtils.cpp | 6 ++++-- test/cmdlineTests/shielded_delete_array/output | 16 ++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index ffd2d1e59..bf693ba17 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -569,11 +569,13 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const { // unroll loop for small arrays @todo choose a good value // Note that we loop over storage slots here, not elements. + bool isShielded = _type.baseType()->isShielded(); + auto storeInstruction = isShielded ? Instruction::CSTORE : Instruction::SSTORE; for (unsigned i = 1; i < _type.storageSize(); ++i) _context - << u256(0) << Instruction::DUP2 << Instruction::SSTORE + << u256(0) << Instruction::DUP2 << storeInstruction << u256(1) << Instruction::ADD; - _context << u256(0) << Instruction::SWAP1 << Instruction::SSTORE; + _context << u256(0) << Instruction::SWAP1 << storeInstruction; } else if (!_type.baseType()->isValueType() && _type.length() <= 4) { diff --git a/test/cmdlineTests/shielded_delete_array/output b/test/cmdlineTests/shielded_delete_array/output index 35385dfab..37bf58973 100644 --- a/test/cmdlineTests/shielded_delete_array/output +++ b/test/cmdlineTests/shielded_delete_array/output @@ -66,13 +66,13 @@ sub_0: assembly { 0x00 dup1 dup1 - sstore + cstore 0x01 dup2 swap1 - sstore + cstore 0x02 - sstore + cstore /* "input.sol":252:315 function clearLarge() public {... */ jump /* "input.sol":321:380 function clearAddrs() public {... */ @@ -83,17 +83,17 @@ sub_0: assembly { 0x0d dup2 swap1 - sstore + cstore 0x0e dup2 swap1 - sstore + cstore 0x0f dup2 swap1 - sstore + cstore 0x10 - sstore + cstore /* "input.sol":252:315 function clearLarge() public {... */ jump tag_7: @@ -140,6 +140,6 @@ sub_0: assembly { swap1 jump // out - auxdata: 0xa2646970667358221220c773b0f9dcaec87d4bc80d443c6d9384f9b062cae07690dfcf1a87d40fb7609264736f6c637828302e382e33312d646576656c6f702e323032362e312e32322b636f6d6d69742e38636561336431360059 + auxdata: 0xa264697066735822122013da03895d972cae40f39a891c290e38db6c2ef2b2111967fa1e665cb15aa49e64736f6c63782c302e382e33312d646576656c6f702e323032362e312e32322b636f6d6d69742e62373237363834392e6d6f64005d } From 6c1bb1a5394978a3ad6ec4dd26087983fc985954 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Tue, 20 Jan 2026 16:47:17 -0500 Subject: [PATCH 13/73] test: add tests for shielded types return visibility Tests are currently failing since right now we only restrict returning shielded addresses and integers. --- .../address/shielded_address_members.sol | 4 +- .../sbytes_external_function_returns.sol | 27 ------------ .../visibility/shielded_address_return.sol | 22 ++++++++++ .../visibility/shielded_array_return.sol | 44 +++++++++++++++++++ .../visibility/shielded_function_internal.sol | 18 -------- .../visibility/shielded_integer_return.sol | 22 ++++++++++ .../visibility/shielded_sbool_return.sol | 22 ++++++++++ .../visibility/shielded_sbytes_return.sol | 39 ++++++++++++++++ .../visibility/shielded_struct_return.sol | 29 ++++++++++++ 9 files changed, 180 insertions(+), 47 deletions(-) delete mode 100644 test/libsolidity/syntaxTests/types/sbytes_external_function_returns.sol create mode 100644 test/libsolidity/syntaxTests/visibility/shielded_address_return.sol create mode 100644 test/libsolidity/syntaxTests/visibility/shielded_array_return.sol delete mode 100644 test/libsolidity/syntaxTests/visibility/shielded_function_internal.sol create mode 100644 test/libsolidity/syntaxTests/visibility/shielded_integer_return.sol create mode 100644 test/libsolidity/syntaxTests/visibility/shielded_sbool_return.sol create mode 100644 test/libsolidity/syntaxTests/visibility/shielded_sbytes_return.sol create mode 100644 test/libsolidity/syntaxTests/visibility/shielded_struct_return.sol diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_members.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_members.sol index b0e433ed0..ba36606e1 100644 --- a/test/libsolidity/syntaxTests/types/address/shielded_address_members.sol +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_members.sol @@ -1,9 +1,9 @@ contract C { - function f() public view returns (saddress) { return address(this); } + function f() public view returns (saddress) { return saddress(this); } function g() public view returns (uint) { return f().balance; } function h() public view returns (bytes memory) { return f().code; } function i() public view returns (uint) { return f().code.length; } function j() public view returns (uint) { return h().length; } } // ---- -// DeclarationError 7492: (51-59): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (51-59): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. diff --git a/test/libsolidity/syntaxTests/types/sbytes_external_function_returns.sol b/test/libsolidity/syntaxTests/types/sbytes_external_function_returns.sol deleted file mode 100644 index 6db3e0f78..000000000 --- a/test/libsolidity/syntaxTests/types/sbytes_external_function_returns.sol +++ /dev/null @@ -1,27 +0,0 @@ -contract C { - // Test that sbytes cannot be returned from external functions - function externalSbytes1() external pure returns (sbytes1) { - return sbytes1(0x01); - } - - function externalSbytes8() external pure returns (sbytes8) { - return sbytes8(0x0102030405060708); - } - - function externalSbytes32() external pure returns (sbytes32) { - return sbytes32(0x0102030405060708091011121314151617181920212223242526272829303132); - } - - // Test that sbytes cannot be returned from public functions - function publicSbytes1() public pure returns (sbytes1) { - return sbytes1(0x01); - } - - function publicSbytes8() public pure returns (sbytes8) { - return sbytes8(0x0102030405060708); - } - - function publicSbytes32() public pure returns (sbytes32) { - return sbytes32(0x0102030405060708091011121314151617181920212223242526272829303132); - } -} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/visibility/shielded_address_return.sol b/test/libsolidity/syntaxTests/visibility/shielded_address_return.sol new file mode 100644 index 000000000..240a991f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/shielded_address_return.sol @@ -0,0 +1,22 @@ +contract Deployer { + saddress private storedAddress; + + function getStoredAddress() internal view returns (saddress) { + return storedAddress; + } + + function getStoredAddressPrivate() private view returns (saddress) { + return storedAddress; + } + + function getStoredAddressFailExternal() external returns (saddress) { + return storedAddress; + } + + function getStoredAddressFail() public returns (saddress) { + return storedAddress; + } +} +// ---- +// TypeError 7492: (333-341): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (434-442): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. diff --git a/test/libsolidity/syntaxTests/visibility/shielded_array_return.sol b/test/libsolidity/syntaxTests/visibility/shielded_array_return.sol new file mode 100644 index 000000000..fd5e78330 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/shielded_array_return.sol @@ -0,0 +1,44 @@ +contract C { + // Test that arrays of shielded types cannot be returned from public/external + function externalShieldedArray() external pure returns (suint256[] memory) { + suint256[] memory arr = new suint256[](1); + arr[0] = suint256(42); + return arr; + } + + function publicShieldedArray() public pure returns (suint256[] memory) { + suint256[] memory arr = new suint256[](1); + arr[0] = suint256(42); + return arr; + } + + // Internal should be allowed + function internalShieldedArray() internal pure returns (suint256[] memory) { + suint256[] memory arr = new suint256[](1); + arr[0] = suint256(42); + return arr; + } + + // Private should be allowed + function privateShieldedArray() private pure returns (suint256[] memory) { + suint256[] memory arr = new suint256[](1); + arr[0] = suint256(42); + return arr; + } + + // Fixed-size arrays should also be blocked + function externalFixedShieldedArray() external pure returns (suint256[3] memory) { + suint256[3] memory arr; + return arr; + } + + function publicFixedShieldedArray() public pure returns (suint256[3] memory) { + suint256[3] memory arr; + return arr; + } +} +// ---- +// TypeError 7492: (155-172): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (341-358): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (1029-1047): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (1171-1189): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. diff --git a/test/libsolidity/syntaxTests/visibility/shielded_function_internal.sol b/test/libsolidity/syntaxTests/visibility/shielded_function_internal.sol deleted file mode 100644 index 871ba2964..000000000 --- a/test/libsolidity/syntaxTests/visibility/shielded_function_internal.sol +++ /dev/null @@ -1,18 +0,0 @@ -contract Deployer { - saddress private storedAddress; - - function getStoredAddress() internal view returns (saddress) { - return storedAddress; - } - - function getStoredAddressFailExternal() external returns (saddress) { - return storedAddress; - } - - function getStoredAddressFail() public returns (saddress) { - return storedAddress; - } -} -// ---- -// DeclarationError 7492: (223-231): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. -// DeclarationError 7492: (324-332): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. diff --git a/test/libsolidity/syntaxTests/visibility/shielded_integer_return.sol b/test/libsolidity/syntaxTests/visibility/shielded_integer_return.sol new file mode 100644 index 000000000..d48133db2 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/shielded_integer_return.sol @@ -0,0 +1,22 @@ +contract Deployer { + sint private storedInteger; + + function getStoredInteger() internal view returns (sint) { + return storedInteger; + } + + function getStoredIntegerPrivate() private view returns (sint) { + return storedInteger; + } + + function getStoredIntegerFailExternal() external returns (sint) { + return storedInteger; + } + + function getStoredIntegerFailPublic() public returns (sint) { + return storedInteger; + } +} +// ---- +// TypeError 7492: (321-325): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (424-428): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. diff --git a/test/libsolidity/syntaxTests/visibility/shielded_sbool_return.sol b/test/libsolidity/syntaxTests/visibility/shielded_sbool_return.sol new file mode 100644 index 000000000..713631dfc --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/shielded_sbool_return.sol @@ -0,0 +1,22 @@ +contract Deployer { + sbool private storedBoolean; + + function getStoredBoolean() internal view returns (sbool) { + return storedBoolean; + } + + function getStoredBooleanPrivate() private view returns (sbool) { + return storedBoolean; + } + + function getStoredBooleanFailExternal() external returns (sbool) { + return storedBoolean; + } + + function getStoredBooleanFailPublic() public returns (sbool) { + return storedBoolean; + } +} +// ---- +// TypeError 7492: (324-329): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (428-433): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. diff --git a/test/libsolidity/syntaxTests/visibility/shielded_sbytes_return.sol b/test/libsolidity/syntaxTests/visibility/shielded_sbytes_return.sol new file mode 100644 index 000000000..2b36eabae --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/shielded_sbytes_return.sol @@ -0,0 +1,39 @@ +contract C { + // Test that sbytes cannot be returned from external functions + function externalSbytes1() external pure returns (sbytes1) { + return sbytes1(0x01); + } + + function externalSbytes16() external pure returns (sbytes16) { + return sbytes16(0x01020304050607080910111213141516); + } + + function externalSbytes32() external pure returns (sbytes32) { + return sbytes32(0x0102030405060708091011121314151617181920212223242526272829303132); + } + + // Test that sbytes cannot be returned from public functions + function publicSbytes1() public pure returns (sbytes1) { + return sbytes1(0x01); + } + + function publicSbytes32() public pure returns (sbytes32) { + return sbytes32(0x0102030405060708091011121314151617181920212223242526272829303132); + } + + // Internal should be allowed + function internalSbytes32() internal pure returns (sbytes32) { + return sbytes32(0x0102030405060708091011121314151617181920212223242526272829303132); + } + + // Private should be allowed + function privateSbytes32() private pure returns (sbytes32) { + return sbytes32(0x0102030405060708091011121314151617181920212223242526272829303132); + } +} +// ---- +// TypeError 7492: (134-141): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (237-245): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (372-380): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (599-606): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (698-706): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. diff --git a/test/libsolidity/syntaxTests/visibility/shielded_struct_return.sol b/test/libsolidity/syntaxTests/visibility/shielded_struct_return.sol new file mode 100644 index 000000000..693984cf8 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/shielded_struct_return.sol @@ -0,0 +1,29 @@ +contract C { + // Struct containing shielded field + struct SecretData { + uint256 publicValue; + suint256 secretValue; + } + + // Test that struct with shielded field cannot be returned from public/external + function externalStructWithShielded() external pure returns (SecretData memory) { + return SecretData(1, suint256(2)); + } + + function publicStructWithShielded() public pure returns (SecretData memory) { + return SecretData(1, suint256(2)); + } + + // Internal should be allowed + function internalStructWithShielded() internal pure returns (SecretData memory) { + return SecretData(1, suint256(2)); + } + + // Private should be allowed + function privateStructWithShielded() private pure returns (SecretData memory) { + return SecretData(1, suint256(2)); + } +} +// ---- +// TypeError 7492: (292-309): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (424-441): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. From b30f6b5b9f49f90bd37e0f2b00929695bed419e1 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:39:07 -0500 Subject: [PATCH 14/73] test: fix semantic tests 4 of the tests were relying on returning shielded types, which now need to be modified to unshield the types before returning them from public functions. --- .../shielded_struct_double_nested_array.sol | 23 +++++++++++++++-- .../shielded_copy_from_mapping_to_mapping.sol | 25 ++++++++++++++++--- ...hielded_mapping_nested_array_in_struct.sol | 23 +++++++++++++++-- .../semanticTests/types/sbytes_storage.sol | 14 +++++------ 4 files changed, 71 insertions(+), 14 deletions(-) diff --git a/test/libsolidity/semanticTests/structs/shielded_struct_double_nested_array.sol b/test/libsolidity/semanticTests/structs/shielded_struct_double_nested_array.sol index 69d65a4b8..15dde5f0b 100644 --- a/test/libsolidity/semanticTests/structs/shielded_struct_double_nested_array.sol +++ b/test/libsolidity/semanticTests/structs/shielded_struct_double_nested_array.sol @@ -20,8 +20,27 @@ contract C { data = S({y: y}); } - function f() public returns (S memory) { - return data; + struct SUnshielded { + uint8[][] y; + } + + function f() public returns (SUnshielded memory) { + // Convert shielded struct to unshielded for public return. + // This is all just type conversion gymnastic to be able to get an output that can be checked in the test framework. + // The actual point of the test is to verify the assignment logic in the constructor above. + return SUnshielded({y: toUnshielded(data.y)}); + } + + function toUnshielded(suint8[][] memory arr) internal pure returns (uint8[][] memory) { + uint8[][] memory result = new uint8[][](arr.length); + for (uint256 i = 0; i < arr.length; i++) { + uint256 innerLen = arr[i].length; + result[i] = new uint8[](innerLen); + for (uint256 j = 0; j < innerLen; j++) { + result[i][j] = uint8(arr[i][j]); + } + } + return result; } } // ---- diff --git a/test/libsolidity/semanticTests/types/mapping/shielded_copy_from_mapping_to_mapping.sol b/test/libsolidity/semanticTests/types/mapping/shielded_copy_from_mapping_to_mapping.sol index 9ce3888eb..5f5e0b850 100644 --- a/test/libsolidity/semanticTests/types/mapping/shielded_copy_from_mapping_to_mapping.sol +++ b/test/libsolidity/semanticTests/types/mapping/shielded_copy_from_mapping_to_mapping.sol @@ -20,11 +20,30 @@ contract C { y[1] = d; src[0] = S({x: [suint8(7), suint8(8), suint8(9)], y: y, z: suint16(13)}); + dst[0] = src[0]; } - function f() public returns (S memory) { - dst[0] = src[0]; - return dst[0]; + struct SUnshielded { + uint8[3] x; + uint8[][] y; + uint16 z; + } + + function f() public returns (SUnshielded memory) { + return toUnshielded(dst[0]); + } + + function toUnshielded(S memory s) internal pure returns (SUnshielded memory) { + uint8[][] memory y = new uint8[][](s.y.length); + for (uint256 i = 0; i < s.y.length; i++) { + uint256 innerLen = s.y[i].length; + y[i] = new uint8[](innerLen); + for (uint256 j = 0; j < innerLen; j++) { + y[i][j] = uint8(s.y[i][j]); + } + } + uint8[3] memory x = [uint8(s.x[0]), uint8(s.x[1]), uint8(s.x[2])]; + return SUnshielded({x: x, y: y, z: uint16(s.z)}); } } // ---- diff --git a/test/libsolidity/semanticTests/types/mapping/shielded_mapping_nested_array_in_struct.sol b/test/libsolidity/semanticTests/types/mapping/shielded_mapping_nested_array_in_struct.sol index c25e03713..f9f264cc5 100644 --- a/test/libsolidity/semanticTests/types/mapping/shielded_mapping_nested_array_in_struct.sol +++ b/test/libsolidity/semanticTests/types/mapping/shielded_mapping_nested_array_in_struct.sol @@ -19,8 +19,27 @@ contract C { src[0] = S({y: y}); } - function f(uint8 index) public returns (S memory) { - return src[index]; + struct SUnshielded { + uint8[][] y; + } + + function f(uint8 index) public returns (SUnshielded memory) { + // Convert shielded struct to unshielded for public return. + // This is all just type conversion gymnastic to be able to get an output that can be checked in the test framework. + // The actual point of the test is to verify the assignment logic in the constructor above. + return SUnshielded({y: toUnshielded(src[index].y)}); + } + + function toUnshielded(suint8[][] memory arr) internal pure returns (uint8[][] memory) { + uint8[][] memory result = new uint8[][](arr.length); + for (uint256 i = 0; i < arr.length; i++) { + uint256 innerLen = arr[i].length; + result[i] = new uint8[](innerLen); + for (uint256 j = 0; j < innerLen; j++) { + result[i][j] = uint8(arr[i][j]); + } + } + return result; } } // ---- diff --git a/test/libsolidity/semanticTests/types/sbytes_storage.sol b/test/libsolidity/semanticTests/types/sbytes_storage.sol index dd0a8ce2a..b477f148e 100644 --- a/test/libsolidity/semanticTests/types/sbytes_storage.sol +++ b/test/libsolidity/semanticTests/types/sbytes_storage.sol @@ -2,27 +2,27 @@ contract C { sbytes1 stored_sb1; sbytes8 stored_sb8; sbytes32 stored_sb32; - + function set_values() public { stored_sb1 = sbytes1(0x01); stored_sb8 = sbytes8(0x0102030405060708); stored_sb32 = sbytes32(0x0102030405060708091011121314151617181920212223242526272829303132); } - - function get_values() public view returns(sbytes1, sbytes8, sbytes32) { + + function get_values() private view returns(sbytes1, sbytes8, sbytes32) { return (stored_sb1, stored_sb8, stored_sb32); } - + function test_storage() public returns(bool) { set_values(); (sbytes1 sb1, sbytes8 sb8, sbytes32 sb32) = get_values(); - + require(sb1 == sbytes1(0x01)); require(sb8 == sbytes8(0x0102030405060708)); require(sb32 == sbytes32(0x0102030405060708091011121314151617181920212223242526272829303132)); - + return true; } } // ---- -// test_storage() -> true \ No newline at end of file +// test_storage() -> true From fd48956dd5a8720db0f3e140ab50a0b5d95bd703 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Tue, 20 Jan 2026 16:48:22 -0500 Subject: [PATCH 15/73] fix(visibility): prevent returning any shielded type in public/external fcts This fixes veridise issue 759. Moved the check from DeclarationTypeChecker to TypeChecker because with the new semantic we need to recursively check all child AST nodes (members of structs for example). Keeping the check in DeclarationTypeChecker was throwing some assert errors on some children nodes that haven't been previously been type checked themselves. Having the check done in TypeChecker also feels more natural anyways, since its checking for return types and there was already a function named checkArgumentAndReturnParameter. --- .../analysis/DeclarationTypeChecker.cpp | 24 ------------------- libsolidity/analysis/TypeChecker.cpp | 15 ++++++++++-- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index 4cd655944..556a423cb 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -549,30 +549,6 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) if ( (_variable.isConstant() || _variable.immutable()) && (type->isShielded())) m_errorReporter.declarationError(7491_error, _variable.location(), "Shielded objects cannot be set to constant or immutable."); - if (_variable.isReturnParameter()) - { - auto const* scope = _variable.scope(); - if (auto const* function = dynamic_cast(scope)) - { - // this error will get catched in another place - if (!function->isConstructor()) - { - Visibility functionVisibility = function->visibility(); - if ( - (functionVisibility == Visibility::Public || functionVisibility == Visibility::External) && - (type->category() == Type::Category::ShieldedInteger || type->category() == Type::Category::ShieldedAddress) - ) - { - m_errorReporter.declarationError( - 7492_error, - _variable.location(), - "Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type." - ); - } - } - } - } - if (_variable.isConstant() && !type->isValueType()) { bool allowed = false; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b928291d8..15e059b55 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -365,6 +365,17 @@ bool TypeChecker::visit(FunctionDefinition const& _function) ); else if (functionIsExternallyVisible) { + // Check for shielded return types in public/external functions. + // Note that constructors can't have return types so the second clause + // in functionIsExternallyVisible (non-abstract constructor) does not apply here. + if (_var.isReturnParameter() && type(_var)->containsShieldedType()) + m_errorReporter.typeError( + 7492_error, + _var.location(), + "Shielded objects cannot be returned from public or external functions. " + "Use internal or private functions or cast to an unshielded type." + ); + auto iType = type(_var)->interfaceType(_function.libraryFunction()); if (!iType) @@ -3494,7 +3505,7 @@ bool TypeChecker::visit(IndexAccess const& _access) { // Always expect uint256 for array indices (this processes the expression) expectType(*index, *TypeProvider::uint256()); - + // Check if index type is shielded and reject it if (type(*index)->isShielded()) { @@ -3504,7 +3515,7 @@ bool TypeChecker::visit(IndexAccess const& _access) "Shielded types are not allowed as array indices." ); } - + if (!m_errorReporter.hasErrors()) { if (auto numberType = dynamic_cast(type(*index))) From 4cfbda5632ccfd4490e8348856a4f7a954942e18 Mon Sep 17 00:00:00 2001 From: cdrappi Date: Thu, 22 Jan 2026 14:48:58 -0500 Subject: [PATCH 16/73] test: update tests that check disallowing implicit conversion of saddress <=> address --- ...s_array_to_address_array_external_call.sol | 15 ++++++++------ ...ess_array_to_address_array_return_type.sol | 20 +++++++++++++++---- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_external_call.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_external_call.sol index 2334b8a6d..c2a49aaaa 100644 --- a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_external_call.sol +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_external_call.sol @@ -4,9 +4,13 @@ contract Helper { function returnsAddressArray() external pure returns (address[] memory) { return new address[](5); } - function returnsSaddressArray() external pure returns (saddress[] memory) { + function returnsSaddressArray() internal pure returns (saddress[] memory) { return new saddress[](5); } + // External wrapper to test implicit conversion of return value + function testReturnsSaddressArrayConversion() external pure { + address[] memory a = returnsSaddressArray(); + } } contract C { @@ -23,11 +27,10 @@ contract C { function testExternalCallReturnAssignment() external view { // Assignment from external call with wrong return type saddress[] memory sa = helper.returnsAddressArray(); - address[] memory a = helper.returnsSaddressArray(); } } // ---- -// TypeError 9553: (655-657): Invalid type for argument in function call. Invalid implicit conversion from saddress[] memory to address[] memory requested. -// TypeError 9553: (694-695): Invalid type for argument in function call. Invalid implicit conversion from address[] memory to saddress[] memory requested. -// TypeError 9574: (841-892): Type address[] memory is not implicitly convertible to expected type saddress[] memory. -// TypeError 9574: (902-952): Type saddress[] memory is not implicitly convertible to expected type address[] memory. +// TypeError 9574: (539-582): Type saddress[] memory is not implicitly convertible to expected type address[] memory. +// TypeError 9553: (848-850): Invalid type for argument in function call. Invalid implicit conversion from saddress[] memory to address[] memory requested. +// TypeError 9553: (887-888): Invalid type for argument in function call. Invalid implicit conversion from address[] memory to saddress[] memory requested. +// TypeError 9574: (1034-1085): Type address[] memory is not implicitly convertible to expected type saddress[] memory. diff --git a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_return_type.sol b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_return_type.sol index b9ec811aa..309ad79a0 100644 --- a/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_return_type.sol +++ b/test/libsolidity/syntaxTests/types/address/saddress_array_to_address_array_return_type.sol @@ -7,25 +7,37 @@ contract C { return sa; } - function returnsSaddressArray() external view returns (saddress[] memory) { + function returnsSaddressArray() internal view returns (saddress[] memory) { // Should fail: returning address[] where saddress[] is expected return a; } + // External wrapper to test the internal function + function testReturnsSaddressArray() external view { + address[] memory result = returnsSaddressArray(); + } + function returnsAddressArrayMemory() external pure returns (address[] memory) { saddress[] memory sa_mem = new saddress[](5); // Should fail: returning saddress[] memory where address[] memory is expected return sa_mem; } - function returnsSaddressArrayMemory() external pure returns (saddress[] memory) { + function returnsSaddressArrayMemory() internal pure returns (saddress[] memory) { address[] memory a_mem = new address[](5); // Should fail: returning address[] memory where saddress[] memory is expected return a_mem; } + + // External wrapper to test the internal function + function testReturnsSaddressArrayMemory() external pure { + address[] memory result = returnsSaddressArrayMemory(); + } } // ---- // TypeError 6359: (216-218): Return argument type saddress[] storage ref is not implicitly convertible to expected type (type of first return variable) address[] memory. // TypeError 6359: (395-396): Return argument type address[] storage ref is not implicitly convertible to expected type (type of first return variable) saddress[] memory. -// TypeError 6359: (645-651): Return argument type saddress[] memory is not implicitly convertible to expected type (type of first return variable) address[] memory. -// TypeError 6359: (899-904): Return argument type address[] memory is not implicitly convertible to expected type (type of first return variable) saddress[] memory. +// TypeError 9574: (523-571): Type saddress[] memory is not implicitly convertible to expected type address[] memory. +// TypeError 6359: (820-826): Return argument type saddress[] memory is not implicitly convertible to expected type (type of first return variable) address[] memory. +// TypeError 6359: (1074-1079): Return argument type address[] memory is not implicitly convertible to expected type (type of first return variable) saddress[] memory. +// TypeError 9574: (1212-1266): Type saddress[] memory is not implicitly convertible to expected type address[] memory. From c55e60eecad4e96454703f071da90f4d2176c47d Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 16:03:53 -0500 Subject: [PATCH 17/73] test: update sstore then cload test expectations to reflect new semantics in revm --- .../semanticTests/inlineAssembly/shielded_sstore_then_cload.sol | 2 +- .../inlineAssembly/sstore_then_cload_no_optimize.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/semanticTests/inlineAssembly/shielded_sstore_then_cload.sol b/test/libsolidity/semanticTests/inlineAssembly/shielded_sstore_then_cload.sol index 5ce8f0086..6eaca4b7f 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/shielded_sstore_then_cload.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/shielded_sstore_then_cload.sol @@ -13,4 +13,4 @@ contract C { } // ---- // f1() -// f() -> FAILURE +// f() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/sstore_then_cload_no_optimize.sol b/test/libsolidity/semanticTests/inlineAssembly/sstore_then_cload_no_optimize.sol index b7bdab03d..c8cd40cf3 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/sstore_then_cload_no_optimize.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/sstore_then_cload_no_optimize.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// f() -> FAILURE +// f() -> 42 From 99ae5ec79d74c4e12ab1c924e265f6bd5ff7497f Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 16:45:26 -0500 Subject: [PATCH 18/73] test: add yul, syntax & semantic tests for timestamp_seconds/timestamp_ms --- .../timestamps/timestamp_via_ir.sol | 34 +++++++++++++++++++ .../types/magic_block_timestamp_ms.sol | 12 +++++++ ...e_assembly_timestampms_disallowed_pure.sol | 9 +++++ .../inline_assembly_timestampms_view.sol | 9 +++++ test/libyul/yulSyntaxTests/timestampms.yul | 5 +++ 5 files changed, 69 insertions(+) create mode 100644 test/libsolidity/semanticTests/timestamps/timestamp_via_ir.sol create mode 100644 test/libsolidity/syntaxTests/types/magic_block_timestamp_ms.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_timestampms_disallowed_pure.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_timestampms_view.sol create mode 100644 test/libyul/yulSyntaxTests/timestampms.yul diff --git a/test/libsolidity/semanticTests/timestamps/timestamp_via_ir.sol b/test/libsolidity/semanticTests/timestamps/timestamp_via_ir.sol new file mode 100644 index 000000000..c280fd5f8 --- /dev/null +++ b/test/libsolidity/semanticTests/timestamps/timestamp_via_ir.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.0; + +// Test that block.timestamp_seconds and block.timestamp_ms work correctly +// when compiled via the IR pipeline. +contract C { + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } + + function getTimestampMs() public view returns (uint256) { + return block.timestamp_ms; + } + + function getTimestampSeconds() public view returns (uint256) { + return block.timestamp_seconds; + } + + function checkTimestampEqualTimestampSeconds() public view returns (bool) { + return block.timestamp == block.timestamp_seconds; + } + + function checkTimestampMsGreaterThanTimestamp() public view returns (bool) { + return block.timestamp_ms >= block.timestamp * 1000; + } +} +// ==== +// compileViaYul: true +// ---- +// getTimestamp() -> 0x0f +// getTimestampMs() -> 0x7530 +// getTimestampSeconds() -> 0x2d +// checkTimestampEqualTimestampSeconds() -> true +// checkTimestampMsGreaterThanTimestamp() -> true diff --git a/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms.sol b/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms.sol new file mode 100644 index 000000000..7753f631f --- /dev/null +++ b/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms.sol @@ -0,0 +1,12 @@ +contract C { + function f() public view returns (uint) { + return block.timestamp; + } + function g() public view returns (uint) { + return block.timestamp_ms; + } + function h() public view returns (uint) { + return block.timestamp_seconds; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_timestampms_disallowed_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_timestampms_disallowed_pure.sol new file mode 100644 index 000000000..90dce372f --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_timestampms_disallowed_pure.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure returns (uint256 tsMs) { + assembly { + tsMs := timestampms() + } + } +} +// ---- +// TypeError 2527: (106-119): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_timestampms_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_timestampms_view.sol new file mode 100644 index 000000000..d38e5417a --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_timestampms_view.sol @@ -0,0 +1,9 @@ +contract C { + function f() public view returns (uint256 ts, uint256 tsMs) { + assembly { + ts := timestamp() + tsMs := timestampms() + } + } +} +// ---- diff --git a/test/libyul/yulSyntaxTests/timestampms.yul b/test/libyul/yulSyntaxTests/timestampms.yul new file mode 100644 index 000000000..a0d53719e --- /dev/null +++ b/test/libyul/yulSyntaxTests/timestampms.yul @@ -0,0 +1,5 @@ +{ + let x := timestamp() + let y := timestampms() +} +// ---- From 05f3c98c5c7b34e3d43d5a3e46c471846997b2a5 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 16:45:26 -0500 Subject: [PATCH 19/73] fix: --via-ir pipeline should support magic block.timestamp_ms and .timestamp_seconds --- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 158ecafb3..ffc41f972 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1971,12 +1971,12 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) // we can ignore the kind of magic and only look at the name of the member if (member == "coinbase") define(_memberAccess) << "coinbase()\n"; - else if (member == "timestamp") + // "timestamp_seconds" is an alias for "timestamp", allowing contract authors + // to be explicit about the unit when used alongside "timestamp_ms" + else if (member == "timestamp" || member == "timestamp_seconds") define(_memberAccess) << "timestamp()\n"; else if (member == "timestamp_ms") - define(_memberAccess) << "timestamp_ms()\n"; - else if (member == "timestamp_seconds") - define(_memberAccess) << "timestamp_seconds()\n"; + define(_memberAccess) << "timestampms()\n"; else if (member == "difficulty" || member == "prevrandao") { if (m_context.evmVersion().hasPrevRandao()) From 09a15ad5cc3ddbf9d06b8672fc68ee17d0b3b0e7 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 17:01:00 -0500 Subject: [PATCH 20/73] test: add EVM version gate test for timestampms instruction Verifies that the timestampms instruction is rejected when targeting EVM versions older than Mercury. --- test/libyul/yulSyntaxTests/timestampms_evm_version.yul | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 test/libyul/yulSyntaxTests/timestampms_evm_version.yul diff --git a/test/libyul/yulSyntaxTests/timestampms_evm_version.yul b/test/libyul/yulSyntaxTests/timestampms_evm_version.yul new file mode 100644 index 000000000..4fa9a9163 --- /dev/null +++ b/test/libyul/yulSyntaxTests/timestampms_evm_version.yul @@ -0,0 +1,7 @@ +{ + let x := timestampms() +} +// ==== +// EVMVersion: Date: Mon, 26 Jan 2026 17:01:04 -0500 Subject: [PATCH 21/73] fix: gate TIMESTAMPMS opcode to Mercury-compatible VMs Add TIMESTAMPMS to the same version gate as CLOAD/CSTORE in both EVMVersion::hasOpcode() and AsmAnalysis. This prevents generation of bytecode containing opcode 0x4b for VMs that don't support it. --- liblangutil/EVMVersion.cpp | 1 + libyul/AsmAnalysis.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index c0c12274c..8908e3727 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -63,6 +63,7 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional _eofVersi return supportsTransientStorage(); case Instruction::CLOAD: case Instruction::CSTORE: + case Instruction::TIMESTAMPMS: return supportShieldedStorage() && !_eofVersion.has_value(); // Instructions below are deprecated in EOF case Instruction::CALL: diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index eefbb6cc2..c9d95f713 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -852,7 +852,7 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio errorForVM(7755_error, "only available for Cancun-compatible"); else if ((_instr == evmasm::Instruction::TSTORE || _instr == evmasm::Instruction::TLOAD) && !m_evmVersion.supportsTransientStorage()) errorForVM(6243_error, "only available for Cancun-compatible"); - else if ((_instr == evmasm::Instruction::CLOAD || _instr == evmasm::Instruction::CSTORE) && !m_evmVersion.supportShieldedStorage()) + else if ((_instr == evmasm::Instruction::CLOAD || _instr == evmasm::Instruction::CSTORE || _instr == evmasm::Instruction::TIMESTAMPMS) && !m_evmVersion.supportShieldedStorage()) errorForVM(6245_error, "only available for Mercury-compatible"); else if (_instr == evmasm::Instruction::PC) m_errorReporter.error( From 8a2c39047102c5ad1eafd037cd7546b6b1f56dbd Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 18:08:07 -0500 Subject: [PATCH 22/73] ci(speedup): cache more steps in the solidity build (#140) --- .github/workflows/test.yml | 70 +++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 901c263f6..2306806e3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,6 +4,7 @@ on: push: branches: - seismic + - zellic-audit pull_request: branches: - seismic @@ -23,22 +24,17 @@ jobs: timeout-minutes: 120 steps: - uses: actions/checkout@v4 + with: + submodules: recursive - uses: dtolnay/rust-toolchain@stable - # TODO: move lower after works - - name: Clone seismic-revm repo - run: | - git clone https://github.com/SeismicSystems/seismic-revm.git /tmp/seismic-revm - echo "SEISMIC_REVM_PATH=/tmp/seismic-revm" >> $GITHUB_ENV - echo "seismic-revm cloned to: /tmp/seismic-revm" - - uses: Swatinem/rust-cache@v2 + + # Restore build cache (based on target branch for PRs, current branch for pushes) + - name: Restore build cache + uses: actions/cache/restore@v4 with: - shared-key: "semantic-test-cache" - workspaces: /tmp/seismic-revm - - name: Build seismic-revm - run: | - cd $SEISMIC_REVM_PATH - cargo build -p revme - echo "SEISMIC_REVME_EXEC=$SEISMIC_REVM_PATH/target/debug/revme" >> $GITHUB_ENV + path: build + key: build-${{ runner.os }}-${{ github.base_ref || github.ref_name }} + - name: Install & configure ccache run: | set -euo pipefail @@ -49,11 +45,10 @@ jobs: mkdir -p ~/.ccache ccache --show-config | grep cache_dir - name: Restore ccache - uses: actions/cache@v4 + uses: actions/cache/restore@v4 with: path: ~/.ccache - key: ccache-${{ hashFiles('**/CMakeLists.txt') }} - restore-keys: ccache- + key: ccache-${{ runner.os }}-${{ github.base_ref || github.ref_name }} - name: Install dependencies run: | @@ -63,6 +58,7 @@ jobs: - name: Build ssolc run: | set -euo pipefail + echo "Building with $(nproc) parallel jobs" mkdir -p build cd build cmake .. -DCMAKE_BUILD_TYPE=Debug \ @@ -72,6 +68,22 @@ jobs: cmake --build . --config Debug -j $(nproc) - name: Run soltest run: ./scripts/soltest.sh + + - name: Clone seismic-revm repo + run: | + git clone https://github.com/SeismicSystems/seismic-revm.git /tmp/seismic-revm + echo "SEISMIC_REVM_PATH=/tmp/seismic-revm" >> $GITHUB_ENV + echo "seismic-revm cloned to: /tmp/seismic-revm" + - uses: Swatinem/rust-cache@v2 + with: + shared-key: "semantic-test-cache" + workspaces: /tmp/seismic-revm + - name: Build seismic-revm + run: | + cd $SEISMIC_REVM_PATH + cargo build -p revme + echo "SEISMIC_REVME_EXEC=$SEISMIC_REVM_PATH/target/debug/revme" >> $GITHUB_ENV + - name: Run semantic tests (no optimizer) run: | set -euo pipefail @@ -88,3 +100,27 @@ jobs: [ -x "$SEISMIC_REVME_EXEC" ] || { echo "Error: revme executable not found at $SEISMIC_REVME_EXEC"; exit 1; } SEMANTIC_TESTS_DIR="$GITHUB_WORKSPACE/test/libsolidity/semanticTests" RUST_LOG=info RUST_BACKTRACE=1 $SEISMIC_REVME_EXEC semantics --keep-going -s "$SSOLC_EXEC" -t "$SEMANTIC_TESTS_DIR" --optimize --optimizer-runs 200 2>&1 + + # Save caches only on push to target branches (after PR merge) + # Delete old caches first since GitHub caches are immutable + - name: Delete old caches + if: github.event_name == 'push' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh cache delete "build-${{ runner.os }}-${{ github.ref_name }}" --repo ${{ github.repository }} || true + gh cache delete "ccache-${{ runner.os }}-${{ github.ref_name }}" --repo ${{ github.repository }} || true + + - name: Save build cache + if: github.event_name == 'push' + uses: actions/cache/save@v4 + with: + path: build + key: build-${{ runner.os }}-${{ github.ref_name }} + + - name: Save ccache + if: github.event_name == 'push' + uses: actions/cache/save@v4 + with: + path: ~/.ccache + key: ccache-${{ runner.os }}-${{ github.ref_name }} From 8a3cecaef286b3a9d3cc24da139e6af9b69b6a80 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 19:01:57 -0500 Subject: [PATCH 23/73] ci(speedup): only reconfigure cmake if something has changed (#143) --- .github/workflows/test.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2306806e3..7f024cdb1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -61,10 +61,17 @@ jobs: echo "Building with $(nproc) parallel jobs" mkdir -p build cd build - cmake .. -DCMAKE_BUILD_TYPE=Debug \ - -DPEDANTIC=OFF \ - -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + # Only run cmake configure if CMakeCache.txt doesn't exist. + # cmake --build will automatically re-run cmake if CMakeLists.txt changed. + if [ ! -f CMakeCache.txt ]; then + echo "No CMakeCache.txt found, running cmake configure..." + cmake .. -DCMAKE_BUILD_TYPE=Debug \ + -DPEDANTIC=OFF \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + else + echo "CMakeCache.txt exists, skipping configure step" + fi cmake --build . --config Debug -j $(nproc) - name: Run soltest run: ./scripts/soltest.sh From 9a9374c33089fded17dce5fc196461bd046f5867 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 17:41:04 -0500 Subject: [PATCH 24/73] test: expect evm version gating for block.timestamp_ms at the Solidity typechecker level --- .../types/magic_block_timestamp_ms.sol | 2 ++ .../types/magic_block_timestamp_ms_error.sol | 16 ++++++++++++++++ .../magic_block_timestamp_seconds_error.sol | 9 +++++++++ 3 files changed, 27 insertions(+) create mode 100644 test/libsolidity/syntaxTests/types/magic_block_timestamp_ms_error.sol create mode 100644 test/libsolidity/syntaxTests/types/magic_block_timestamp_seconds_error.sol diff --git a/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms.sol b/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms.sol index 7753f631f..1adbc2405 100644 --- a/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms.sol +++ b/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms.sol @@ -9,4 +9,6 @@ contract C { return block.timestamp_seconds; } } +// ==== +// EVMVersion: >=mercury // ---- diff --git a/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms_error.sol b/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms_error.sol new file mode 100644 index 000000000..09862da32 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/magic_block_timestamp_ms_error.sol @@ -0,0 +1,16 @@ +contract C { + function f() public view returns (uint) { + return block.timestamp_ms; + } + function g() public view returns (uint ret) { + assembly { + ret := timestampms() + } + } +} +// ==== +// EVMVersion: =cancun +// ---- +// TypeError 4521: (74-92): "timestamp_ms" is not supported by the VM version. +// TypeError 6245: (188-199): The "timestampms" instruction is only available for Mercury-compatible VMs (you are currently compiling for "cancun"). +// DeclarationError 8678: (181-201): Variable count for assignment to "ret" does not match number of values (1 vs. 0) diff --git a/test/libsolidity/syntaxTests/types/magic_block_timestamp_seconds_error.sol b/test/libsolidity/syntaxTests/types/magic_block_timestamp_seconds_error.sol new file mode 100644 index 000000000..1584bca5a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/magic_block_timestamp_seconds_error.sol @@ -0,0 +1,9 @@ +contract C { + function f() public view returns (uint) { + return block.timestamp_seconds; + } +} +// ==== +// EVMVersion: =cancun +// ---- +// TypeError 8743: (74-97): "timestamp_seconds" is not supported by the VM version. From dc937a76d4228db82885e7e516a9c9e75672dc3c Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 18:09:30 -0500 Subject: [PATCH 25/73] fix: gate block.timestamp_ms and block.timestamp_seconds in TypeChecker Add EVM version checks in TypeChecker.cpp for timestamp_ms and timestamp_seconds magic block members. Users targeting non-Mercury VMs will now get a clear compile-time error instead of generating bytecode with invalid opcodes that crash at runtime. --- liblangutil/EVMVersion.h | 1 + libsolidity/analysis/TypeChecker.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 2b367fdb2..f6e7310c5 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -148,6 +148,7 @@ class EVMVersion bool hasMcopy() const { return *this >= cancun(); } bool supportsTransientStorage() const { return *this >= cancun(); } bool supportShieldedStorage() const { return *this >= mercury(); } + bool hasTimestampMs() const { return *this >= mercury(); } bool supportsEOF() const { return *this >= firstWithEOF(); } bool hasOpcode(evmasm::Instruction _opcode, std::optional _eofVersion) const; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 15e059b55..6ed4bca6b 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3451,6 +3451,18 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) _memberAccess.location(), "Since the VM version paris, \"difficulty\" was replaced by \"prevrandao\", which now returns a random number based on the beacon chain." ); + else if (memberName == "timestamp_ms" && !m_evmVersion.hasTimestampMs()) + m_errorReporter.typeError( + 4521_error, + _memberAccess.location(), + "\"timestamp_ms\" is not supported by the VM version." + ); + else if (memberName == "timestamp_seconds" && !m_evmVersion.hasTimestampMs()) + m_errorReporter.typeError( + 8743_error, + _memberAccess.location(), + "\"timestamp_seconds\" is not supported by the VM version." + ); } } From a6dc90071e6d7641ebac7f0ee0d42aebde4cda99 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 19:27:49 -0500 Subject: [PATCH 26/73] test: add SMT test for block.timestamp_seconds aliasing bug The SMT model creates separate symbolic variables for block.timestamp and block.timestamp_seconds even though they resolve to the same opcode. This causes `assert(block.timestamp == block.timestamp_seconds)` to fail spuriously in the SMT checker. --- .../special/timestamp_seconds.sol | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/libsolidity/smtCheckerTests/special/timestamp_seconds.sol diff --git a/test/libsolidity/smtCheckerTests/special/timestamp_seconds.sol b/test/libsolidity/smtCheckerTests/special/timestamp_seconds.sol new file mode 100644 index 000000000..613b7431f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/timestamp_seconds.sol @@ -0,0 +1,16 @@ +contract C +{ + function f(uint timestamp_seconds) public view { + assert(block.timestamp_seconds == timestamp_seconds); // should fail + assert(block.timestamp == timestamp_seconds); // should fail + assert(block.timestamp == block.timestamp_seconds); // should hold, but fails due to SMT model bug + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// EVMVersion: >=mercury +// ---- +// Warning 6328: (63-113): CHC: Assertion violation happens here. +// Warning 6328: (132-174): CHC: Assertion violation happens here. +// Warning 6328: (193-243): CHC: Assertion violation happens here. From 59a419562088e2392ef7ebfc2b05720a067c8e15 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 19:28:06 -0500 Subject: [PATCH 27/73] fix: alias block.timestamp_seconds to block.timestamp in SMT model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SMT encoder was creating separate symbolic variables for block.timestamp and block.timestamp_seconds, even though they resolve to the same TIMESTAMP opcode at runtime. This could cause spurious assertion failures when both were used in the same contract. Following the existing pattern for difficulty/prevrandao aliasing: - SMTEncoder.cpp: alias timestamp_seconds → timestamp in both Identifier and MagicType code paths - Predicate.cpp: alias in TxVarsVisitor for counterexample formatting - SymbolicState.cpp: remove independent constraint for timestamp_seconds - SymbolicTypes.cpp: remove timestamp_seconds from transaction member types --- libsolidity/formal/Predicate.cpp | 2 ++ libsolidity/formal/SMTEncoder.cpp | 9 ++++++++- libsolidity/formal/SymbolicState.cpp | 1 - libsolidity/formal/SymbolicTypes.cpp | 1 - .../smtCheckerTests/special/timestamp_seconds.sol | 4 ++-- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index 7f742ec18..649aea00d 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -252,6 +252,8 @@ std::string Predicate::formatSummaryCall( // TODO remove this for 0.9.0 if (magicKind == MagicType::Kind::Block && memberName == "difficulty") memberName = "prevrandao"; + if (magicKind == MagicType::Kind::Block && memberName == "timestamp_seconds") + memberName = "timestamp"; if (magicKind == MagicType::Kind::Block || magicKind == MagicType::Kind::Message || magicKind == MagicType::Kind::Transaction) txVars.insert(magicType->toString(true) + "." + memberName); diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 57b155743..98b758781 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -1420,13 +1420,20 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) // TODO remove this for 0.9.0 if (name == "block" && memberName == "difficulty") memberName = "prevrandao"; + if (name == "block" && memberName == "timestamp_seconds") + memberName = "timestamp"; defineExpr(_memberAccess, state().txMember(name + "." + memberName)); } else if (auto magicType = dynamic_cast(exprType)) { if (magicType->kind() == MagicType::Kind::Block) - defineExpr(_memberAccess, state().txMember("block." + _memberAccess.memberName())); + { + auto memberName = _memberAccess.memberName(); + if (memberName == "timestamp_seconds") + memberName = "timestamp"; + defineExpr(_memberAccess, state().txMember("block." + memberName)); + } else if (magicType->kind() == MagicType::Kind::Message) defineExpr(_memberAccess, state().txMember("msg." + _memberAccess.memberName())); else if (magicType->kind() == MagicType::Kind::Transaction) diff --git a/libsolidity/formal/SymbolicState.cpp b/libsolidity/formal/SymbolicState.cpp index 35828a3a3..4f290aaf9 100644 --- a/libsolidity/formal/SymbolicState.cpp +++ b/libsolidity/formal/SymbolicState.cpp @@ -235,7 +235,6 @@ smtutil::Expression SymbolicState::txTypeConstraints() const smt::symbolicUnknownConstraints(m_tx.member("block.number"), TypeProvider::uint256()) && smt::symbolicUnknownConstraints(m_tx.member("block.timestamp"), TypeProvider::uint256()) && smt::symbolicUnknownConstraints(m_tx.member("block.timestamp_ms"), TypeProvider::uint256()) && - smt::symbolicUnknownConstraints(m_tx.member("block.timestamp_seconds"), TypeProvider::uint256()) && smt::symbolicUnknownConstraints(m_tx.member("msg.sender"), TypeProvider::address()) && smt::symbolicUnknownConstraints(m_tx.member("msg.value"), TypeProvider::uint256()) && smt::symbolicUnknownConstraints(m_tx.member("tx.origin"), TypeProvider::address()) && diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index 19a9020f6..ca8f2ad31 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -686,7 +686,6 @@ std::map transactionMemberTypes() {"block.number", TypeProvider::uint256()}, {"block.timestamp", TypeProvider::uint256()}, {"block.timestamp_ms", TypeProvider::uint256()}, - {"block.timestamp_seconds", TypeProvider::uint256()}, {"blobhash", TypeProvider::array(DataLocation::Memory, TypeProvider::uint256())}, {"blockhash", TypeProvider::array(DataLocation::Memory, TypeProvider::uint256())}, {"msg.data", TypeProvider::bytesCalldata()}, diff --git a/test/libsolidity/smtCheckerTests/special/timestamp_seconds.sol b/test/libsolidity/smtCheckerTests/special/timestamp_seconds.sol index 613b7431f..075c35572 100644 --- a/test/libsolidity/smtCheckerTests/special/timestamp_seconds.sol +++ b/test/libsolidity/smtCheckerTests/special/timestamp_seconds.sol @@ -3,7 +3,7 @@ contract C function f(uint timestamp_seconds) public view { assert(block.timestamp_seconds == timestamp_seconds); // should fail assert(block.timestamp == timestamp_seconds); // should fail - assert(block.timestamp == block.timestamp_seconds); // should hold, but fails due to SMT model bug + assert(block.timestamp == block.timestamp_seconds); // should hold } } // ==== @@ -13,4 +13,4 @@ contract C // ---- // Warning 6328: (63-113): CHC: Assertion violation happens here. // Warning 6328: (132-174): CHC: Assertion violation happens here. -// Warning 6328: (193-243): CHC: Assertion violation happens here. +// Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. From 2fca444c80720d03a1b7e29c16d636de1d38c647 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 19:48:50 -0500 Subject: [PATCH 28/73] ci: don't run CI on PRs to seismic branch in the zellic-audit branch --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f024cdb1..508cf9894 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,9 @@ on: - zellic-audit pull_request: branches: - - seismic + # NOTE: seismic temporarily removed to avoid duplicate builds from zellic-audit→seismic PR. + # Re-enable after zellic-audit merges to seismic. + # - seismic - zellic-audit concurrency: From 6c84c4a174eafb799a3b9ef422bd012e3d462a76 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Mon, 26 Jan 2026 21:18:58 -0500 Subject: [PATCH 29/73] ci: remove double apt-get; print ccache stats (#145) --- .github/workflows/test.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 508cf9894..c3e5c418c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,4 @@ +# CI workflow for seismic-solidity name: Tests on: @@ -37,30 +38,35 @@ jobs: path: build key: build-${{ runner.os }}-${{ github.base_ref || github.ref_name }} - - name: Install & configure ccache + # Combined dependency installation (saves ~15-20s by avoiding duplicate apt-get update) + - name: Install dependencies & ccache run: | set -euo pipefail sudo apt-get update - sudo apt-get install -y ccache - ccache --set-config=cache_dir=$HOME/.ccache - ccache --set-config=max_size=1G + sudo apt-get install -y ccache build-essential python3 python3-pip zlib1g-dev libboost-all-dev libssl-dev mkdir -p ~/.ccache - ccache --show-config | grep cache_dir + - name: Restore ccache uses: actions/cache/restore@v4 with: path: ~/.ccache key: ccache-${{ runner.os }}-${{ github.base_ref || github.ref_name }} - - name: Install dependencies + # Configure ccache AFTER restore to ensure settings aren't overwritten + - name: Configure ccache run: | - sudo apt-get update - sudo apt-get install -y build-essential python3 python3-pip zlib1g-dev libboost-all-dev libssl-dev + ccache --set-config=cache_dir=$HOME/.ccache + ccache --set-config=max_size=2G + # CRITICAL: hash_dir=false allows cache hits even when build paths differ between CI runs + ccache --set-config=hash_dir=false + ccache --set-config=base_dir=$GITHUB_WORKSPACE - name: Build ssolc run: | set -euo pipefail echo "Building with $(nproc) parallel jobs" + echo "=== ccache stats BEFORE build ===" + ccache -s mkdir -p build cd build # Only run cmake configure if CMakeCache.txt doesn't exist. @@ -75,6 +81,8 @@ jobs: echo "CMakeCache.txt exists, skipping configure step" fi cmake --build . --config Debug -j $(nproc) + echo "=== ccache stats AFTER build ===" + ccache -s - name: Run soltest run: ./scripts/soltest.sh From ad883e156c3a8ab38cb438fb475b122cf60a4280 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:29:10 -0500 Subject: [PATCH 30/73] feat(analysis): add shielded storage opcode semantics to type-checker (#136) Add some basic shielded storage type checking to solc's analysis phase so as to prevent compilation of programs that would clearly halt at runtime (for example trying to sload a private slot). This is effectively adding (very basic) type checking for the new opcode semantic rules that were recently changed in revm: https://github.com/SeismicSystems/seismic-revm/pull/180 ## Tests Moved a few semantic tests to become syntaxTests, since the same programs now fail to compile (with a type error) and so can't be input to revm in semantic tests. ## Notes This PR will affect https://github.com/SeismicSystems/seismic-solidity/pull/112 since the semantic tests there will no longer be able to be used since they won't type check... 2 solutions: 1. we merge that PR without tests 2. we try to come up with some test that would trick the basic type-checker implemented by this PR yet still generate a CSTORE followed by SSTORE that would then need to fail at runtime in revm. Question at that point though is whether that semantic test shouldnt become a syntax test and the type-checker here shouldnt be augmented to catch that... :D --- libsolidity/analysis/TypeChecker.cpp | 149 ++++++++++++++++++ libsolidity/analysis/TypeChecker.h | 5 + libsolidity/ast/ASTAnnotations.h | 3 + scripts/soltest.sh | 3 +- .../cstore_then_sload_no_optimize.sol | 10 -- .../shielded_cstore_then_sload.sol | 16 -- .../shielded_cload_on_suint_ok.sol | 9 ++ .../shielded_cload_then_sstore_ok.sol | 10 ++ .../shielded_cstore_on_suint_ok.sol | 9 ++ .../shielded_cstore_then_sload.sol | 10 ++ .../shielded_cstore_then_sstore.sol | 10 ++ .../shielded_sload_on_suint.sol | 10 ++ .../shielded_sload_then_cstore_ok.sol | 10 ++ .../shielded_sstore_on_suint.sol | 10 ++ .../shielded_sstore_then_cstore_ok.sol | 10 ++ 15 files changed, 247 insertions(+), 27 deletions(-) delete mode 100644 test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_no_optimize.sol delete mode 100644 test/libsolidity/semanticTests/inlineAssembly/shielded_cstore_then_sload.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_cload_on_suint_ok.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_cload_then_sstore_ok.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_on_suint_ok.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sstore.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_on_suint.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_then_cstore_ok.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_suint.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_then_cstore_ok.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 6ed4bca6b..16632c0db 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -854,6 +855,19 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return false; } } + // Track if the variable is shielded for storage operation validation + if (suffix == "slot") + { + // For dynamic arrays, .slot contains the length (not shielded) + // The actual shielded elements are stored at keccak256(slot) + index + // For other types, .slot contains the actual shielded data + // Note: if we ever change the semantics of the length slot and also make it shielded, then we'd need to change this. + if (auto const* arrayType = dynamic_cast(var->type()); + arrayType && arrayType->isDynamicallySized()) + identifierInfo.isShieldedStorage = false; + else + identifierInfo.isShieldedStorage = var->type()->isShielded() || var->type()->containsShieldedType(); + } } else if ( auto const* arrayType = dynamic_cast(var->type()); @@ -966,9 +980,144 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) _inlineAssembly.annotation().hasMemoryEffects = lvalueAccessToMemoryVariable || (analyzer.sideEffects().memory != yul::SideEffects::None); + + validateShieldedStorageOps(_inlineAssembly); + return false; } +void TypeChecker::validateShieldedStorageOps(InlineAssembly const& _inlineAssembly) +{ + auto const& externalRefs = _inlineAssembly.annotation().externalReferences; + + // Track storage operations: slot key -> (isShielded, location, funcName) + struct StorageOp { + bool isShielded; + langutil::SourceLocation location; + std::string funcName; + }; + std::map> storageOps; + + // Helper to get a string key for a slot expression + auto getSlotKey = [](yul::Expression const& _expr) -> std::optional { + if (auto const* lit = std::get_if(&_expr)) + { + if (lit->kind == yul::LiteralKind::Number) + return "lit:" + lit->value.value().str(); + } + else if (auto const* ident = std::get_if(&_expr)) + { + return "var:" + ident->name.str(); + } + return std::nullopt; + }; + + // Helper to check and record a storage operation + auto checkStorageOp = [&](yul::FunctionCall const* funCall) { + std::string_view funcName = yul::resolveFunctionName(funCall->functionName, _inlineAssembly.dialect()); + bool isShieldedOp = (funcName == "cstore" || funcName == "cload"); + bool isNonShieldedOp = (funcName == "sstore" || funcName == "sload"); + + if ((isShieldedOp || isNonShieldedOp) && !funCall->arguments.empty()) + { + auto const& slotExpr = funCall->arguments.front(); + + // Check for external identifier referencing shielded variable with wrong op + if (isNonShieldedOp) + { + if (auto const* slotIdent = std::get_if(&slotExpr)) + { + auto it = externalRefs.find(slotIdent); + if (it != externalRefs.end() && it->second.isShieldedStorage) + { + m_errorReporter.typeError( + 5765_error, + nativeLocationOf(*funCall), + std::string("Cannot use ") + std::string(funcName) + "() on shielded storage variable. Use " + + (funcName == "sstore" ? "cstore" : "cload") + "() instead." + ); + } + } + } + + // Track for same-slot conflict detection + if (auto slotKey = getSlotKey(slotExpr)) + { + storageOps[*slotKey].push_back({isShieldedOp, nativeLocationOf(*funCall), std::string(funcName)}); + } + } + }; + + std::function collectStorageOps = [&](yul::Block const& _block) { + for (auto const& statement : _block.statements) + { + std::visit(util::GenericVisitor{ + [&](yul::ExpressionStatement const& _exprStmt) { + if (auto const* funCall = std::get_if(&_exprStmt.expression)) + checkStorageOp(funCall); + }, + [&](yul::VariableDeclaration const& _varDecl) { + if (_varDecl.value) + if (auto const* funCall = std::get_if(_varDecl.value.get())) + checkStorageOp(funCall); + }, + [&](yul::Assignment const& _assignment) { + if (_assignment.value) + if (auto const* funCall = std::get_if(_assignment.value.get())) + checkStorageOp(funCall); + }, + [&](yul::If const& _if) { + collectStorageOps(_if.body); + }, + [&](yul::Switch const& _switch) { + for (auto const& _case : _switch.cases) + collectStorageOps(_case.body); + }, + [&](yul::ForLoop const& _forLoop) { + collectStorageOps(_forLoop.pre); + collectStorageOps(_forLoop.body); + collectStorageOps(_forLoop.post); + }, + [&](yul::Block const& _nestedBlock) { + collectStorageOps(_nestedBlock); + }, + [&](yul::FunctionDefinition const& _funDef) { + collectStorageOps(_funDef.body); + }, + [](auto const&) {} + }, statement); + } + }; + collectStorageOps(_inlineAssembly.operations().root()); + + // Check for conflicts: cstore makes a slot private, after which sstore/sload will fail + // Note: cload can read from any slot (public or private), so it doesn't conflict + for (auto const& [slotKey, ops] : storageOps) + { + StorageOp const* firstCstore = nullptr; + + for (auto const& op : ops) + { + if (op.funcName == "cstore" && !firstCstore) + { + firstCstore = &op; + } + else if (firstCstore && (op.funcName == "sstore" || op.funcName == "sload")) + { + // cstore was called before sstore/sload on the same slot + // cstore makes the slot private, and sstore/sload cannot access private slots + m_errorReporter.typeError( + 5768_error, + op.location, + "Cannot use " + op.funcName + "() on a slot that was previously written with cstore(). " + "cstore() makes the slot private, and " + op.funcName + "() cannot access private storage. " + "Use " + (op.funcName == "sstore" ? "cstore" : "cload") + "() instead." + ); + } + } + } +} + bool TypeChecker::visit(IfStatement const& _ifStatement) { expectBoolOrShieldedBool(_ifStatement.condition()); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 64401535a..9622c2176 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -132,6 +132,11 @@ class TypeChecker: private ASTConstVisitor bool visit(ErrorDefinition const& _errorDef) override; void endVisit(FunctionTypeName const& _funType) override; bool visit(InlineAssembly const& _inlineAssembly) override; + /// Validates that shielded/non-shielded storage operations (cstore/cload vs sstore/sload) + /// are used consistently within inline assembly. Reports errors when: + /// - sstore/sload is used on a shielded variable reference + /// - sstore/sload is used on a slot that was previously written with cstore + void validateShieldedStorageOps(InlineAssembly const& _inlineAssembly); bool visit(IfStatement const& _ifStatement) override; void endVisit(TryStatement const& _tryStatement) override; bool visit(WhileStatement const& _whileStatement) override; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 7adfc7a4a..6dcf93f61 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -223,6 +223,9 @@ struct InlineAssemblyAnnotation: StatementAnnotation /// Suffix used, one of "slot", "offset", "length", "address", "selector" or empty. std::string suffix; size_t valueSize = size_t(-1); + /// True if the referenced variable's type is shielded (e.g., suint, sbool). + /// Used to validate that shielded variables use cstore/cload instead of sstore/sload. + bool isShieldedStorage = false; }; /// Mapping containing resolved references to external identifiers and their value size diff --git a/scripts/soltest.sh b/scripts/soltest.sh index 34e5653a7..498276ba6 100755 --- a/scripts/soltest.sh +++ b/scripts/soltest.sh @@ -53,7 +53,8 @@ do BOOST_OPTIONS+=(-t "$1") ;; --show-progress | -p) - BOOST_OPTIONS+=("$1") + # boost only recognizes --show-progress, not -p + BOOST_OPTIONS+=("--show-progress") ;; *) SOLTEST_OPTIONS+=("$1") diff --git a/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_no_optimize.sol b/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_no_optimize.sol deleted file mode 100644 index 7119b9ed8..000000000 --- a/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_no_optimize.sol +++ /dev/null @@ -1,10 +0,0 @@ -contract C { - function f() public returns (uint ret) { - assembly { - cstore(0, 42) - ret := sload(0) - } - } -} -// ---- -// f() -> FAILURE diff --git a/test/libsolidity/semanticTests/inlineAssembly/shielded_cstore_then_sload.sol b/test/libsolidity/semanticTests/inlineAssembly/shielded_cstore_then_sload.sol deleted file mode 100644 index a784aae71..000000000 --- a/test/libsolidity/semanticTests/inlineAssembly/shielded_cstore_then_sload.sol +++ /dev/null @@ -1,16 +0,0 @@ -contract C { - suint256 a; - - function f1() public { - a = suint(7); - } - - function f() public returns (uint256 x) { - assembly { - x := sload(a.slot) - } - } -} -// ---- -// f1() -// f() -> FAILURE diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_cload_on_suint_ok.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cload_on_suint_ok.sol new file mode 100644 index 000000000..d4dbe6b93 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cload_on_suint_ok.sol @@ -0,0 +1,9 @@ +contract C { + suint x; + function f() external view returns (uint r) { + assembly { + r := cload(x.slot) + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_cload_then_sstore_ok.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cload_then_sstore_ok.sol new file mode 100644 index 000000000..81b644bf0 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cload_then_sstore_ok.sol @@ -0,0 +1,10 @@ +// cload then sstore: passes syntax check (cload doesn't make slot private) +contract C { + function test() external { + assembly { + pop(cload(0)) + sstore(0, 1) + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_on_suint_ok.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_on_suint_ok.sol new file mode 100644 index 000000000..1aa2d0127 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_on_suint_ok.sol @@ -0,0 +1,9 @@ +contract C { + suint x; + function f() external { + assembly { + cstore(x.slot, 42) + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload.sol new file mode 100644 index 000000000..7c0ca6481 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload.sol @@ -0,0 +1,10 @@ +contract C { + function test() external returns (uint256 r) { + assembly { + cstore(0, 1) + r := sload(0) + } + } +} +// ---- +// TypeError 5768: (125-133): Cannot use sload() on a slot that was previously written with cstore(). cstore() makes the slot private, and sload() cannot access private storage. Use cload() instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sstore.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sstore.sol new file mode 100644 index 000000000..116c0947f --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sstore.sol @@ -0,0 +1,10 @@ +contract C { + function test() external { + assembly { + cstore(0, 1) + sstore(0, 0x1337) + } + } +} +// ---- +// TypeError 5768: (100-117): Cannot use sstore() on a slot that was previously written with cstore(). cstore() makes the slot private, and sstore() cannot access private storage. Use cstore() instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_on_suint.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_on_suint.sol new file mode 100644 index 000000000..84d6caa3e --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_on_suint.sol @@ -0,0 +1,10 @@ +contract C { + suint x; + function f() external view returns (uint r) { + assembly { + r := sload(x.slot) + } + } +} +// ---- +// TypeError 5765: (112-125): Cannot use sload() on shielded storage variable. Use cload() instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_then_cstore_ok.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_then_cstore_ok.sol new file mode 100644 index 000000000..fa3980c91 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_then_cstore_ok.sol @@ -0,0 +1,10 @@ +// sload then cstore: passes syntax check (runtime error only if slot had non-zero value) +contract C { + function test() external { + assembly { + pop(sload(0)) + cstore(0, 1) + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_suint.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_suint.sol new file mode 100644 index 000000000..8d6a3b254 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_suint.sol @@ -0,0 +1,10 @@ +contract C { + suint x; + function f() external { + assembly { + sstore(x.slot, 42) + } + } +} +// ---- +// TypeError 5765: (85-103): Cannot use sstore() on shielded storage variable. Use cstore() instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_then_cstore_ok.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_then_cstore_ok.sol new file mode 100644 index 000000000..e6368bea0 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_then_cstore_ok.sol @@ -0,0 +1,10 @@ +// sstore then cstore: passes syntax check (runtime error only if sstore wrote non-zero) +contract C { + function test() external { + assembly { + sstore(0, 0) + cstore(0, 1) + } + } +} +// ---- From bf5a8b81b53019892f7b389f62aeaf587d9303d3 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Tue, 27 Jan 2026 10:09:45 -0500 Subject: [PATCH 31/73] delete cstore->cload sem test (is now syntax test) --- .../inlineAssembly/cstore_cload_then_sload_cse.sol | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 test/libsolidity/semanticTests/inlineAssembly/cstore_cload_then_sload_cse.sol diff --git a/test/libsolidity/semanticTests/inlineAssembly/cstore_cload_then_sload_cse.sol b/test/libsolidity/semanticTests/inlineAssembly/cstore_cload_then_sload_cse.sol deleted file mode 100644 index 10b690909..000000000 --- a/test/libsolidity/semanticTests/inlineAssembly/cstore_cload_then_sload_cse.sol +++ /dev/null @@ -1,13 +0,0 @@ -contract C { - function test() external returns (uint256) { - assembly { - cstore(0, 0x1337) - let a := cload(0) - let b := sload(0) - mstore(0, add(a, b)) - return(0, 0x20) - } - } -} -// ---- -// test() -> FAILURE From 8fb3c2ab95e9e98540febe120eccc2bdf57db250 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 27 Jan 2026 16:38:32 -0500 Subject: [PATCH 32/73] fix(optimiser): cstore shouldn't be eliminated when followed by sstore (#112) Fixes https://github.com/SeismicSystems/internal/issues/177. Now that https://github.com/SeismicSystems/seismic-solidity/pull/136 had been merged, simple intra-block cstore/store semantics are prevented. However more complex cstore/sstore (such as in different assembly blocks) are not caught by the type checker. So we still want to make sure the optimizer doesn't remove SLOADs that could possibly revert at runtime. This PR achives (at least I think...) that in both: 1. the evmasm optimiser (when solc run without --via-ir) 2. libyul optimiser (when run with --via-ir) --------- Co-authored-by: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> --- libevmasm/CommonSubexpressionEliminator.cpp | 26 ++++++-- libevmasm/KnownState.cpp | 29 +++++--- libevmasm/KnownState.h | 2 +- libyul/optimiser/UnusedStoreEliminator.cpp | 66 ++++++++++++++++++- libyul/optimiser/UnusedStoreEliminator.h | 6 ++ test/libevmasm/Optimiser.cpp | 9 ++- .../cstore_then_sstore_should_fail.sol | 16 +++++ .../sstore_zero_then_cstore.sol | 21 ++++++ 8 files changed, 154 insertions(+), 21 deletions(-) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/cstore_then_sstore_should_fail.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/sstore_zero_then_cstore.sol diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 485afc699..f77a05df5 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -243,13 +243,31 @@ void CSECodeGenerator::addDependencies(Id _c) // this loads an unknown value from storage or memory and thus, in addition to its // arguments, depends on all store operations to addresses where we do not know that // they are different that occur before this load - StoreOperation::Target target = (expr.item->instruction() == Instruction::SLOAD - || expr.item->instruction() == Instruction::CLOAD) ? - StoreOperation::Storage : StoreOperation::Memory; + StoreOperation::Target target; + switch (expr.item->instruction()) + { + case Instruction::SLOAD: + // CLOAD can read both public and private storage, so its treated separately below. + // We still need it here however to avoid the default assert. + case Instruction::CLOAD: + target = StoreOperation::Storage; + break; + case Instruction::MLOAD: + case Instruction::KECCAK256: + target = StoreOperation::Memory; + break; + default: + solAssert(false, "Unexpected load instruction in CSE dependency analysis"); + } Id slotToLoadFrom = expr.arguments.at(0); for (auto const& p: m_storeOperations) { - if (p.first.first != target) + // CLOAD can read both public and private storage, so check both domains + bool shouldCheckTarget + = (expr.item->instruction() == Instruction::CLOAD) + ? (p.first.first == StoreOperation::Storage || p.first.first == StoreOperation::ShieldedStorage) + : (p.first.first == target); + if (!shouldCheckTarget) continue; Id slot = p.first.second; StoreOperations const& storeOps = p.second; diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index da3a052af..445b78070 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -179,7 +179,7 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool m_stackHeight + static_cast(_item.deposit()), loadFromStorage(arguments[0], _item.debugData()) ); - break; + break; case Instruction::CLOAD: setStackElement( m_stackHeight + static_cast(_item.deposit()), @@ -356,8 +356,13 @@ KnownState::StoreOperation KnownState::storeInStorage( // Copy over all values (i.e. retain knowledge about them) where we know that this store // operation will not destroy the knowledge. Specifically, we copy storage locations we know // are different from _slot or locations where we know that the stored value is equal to _value. + // + // Also preserve storage items from different storage domains (private vs public), as they + // cannot overwrite each other and accessing the same slot from different domains causes a runtime error. for (auto const& storageItem: m_storageContent) - if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || storageItem.second.value == _value) + if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || + storageItem.second.value == _value || + storageItem.second.is_private) // Public store cannot overwrite private storage storageContents.insert(storageItem); m_storageContent = std::move(storageContents); @@ -385,14 +390,19 @@ KnownState::StoreOperation KnownState::storeInShieldedStorage( // Copy over all values (i.e. retain knowledge about them) where we know that this store // operation will not destroy the knowledge. Specifically, we copy storage locations we know // are different from _slot or locations where we know that the stored value is equal to _value. + // + // CSTORE can claim a public slot if its value is 0, so we cannot unconditionally preserve + // public storage knowledge about the same slot. We only preserve knowledge about different slots + // or if we're storing the exact same private value. for (auto const& storageItem: m_storageContent) - if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || storageItem.second.value == _value) + if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || + storageItem.second == FlaggedStorage{_value, true}) storageContents.insert(storageItem); m_storageContent = std::move(storageContents); AssemblyItem item(Instruction::CSTORE, std::move(_debugData)); Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber); - StoreOperation operation{StoreOperation::Storage, _slot, m_sequenceNumber, id}; + StoreOperation operation{StoreOperation::ShieldedStorage, _slot, m_sequenceNumber, id}; m_storageContent[_slot] = {_value, true}; // increment a second time so that we get unique sequence numbers for writes m_sequenceNumber++; @@ -413,13 +423,16 @@ ExpressionClasses::Id KnownState::loadFromStorage(Id _slot, langutil::DebugData: ExpressionClasses::Id KnownState::loadFromShieldedStorage(Id _slot, langutil::DebugData::ConstPtr _debugData) { - if (m_storageContent.count(_slot) && m_storageContent.at(_slot).is_private) + // CLOAD can read both public and private storage + if (m_storageContent.count(_slot)) return m_storageContent.at(_slot).value; + // No prior knowledge about this slot - create a fresh CLOAD expression. + // Unlike loadFromStorage, we intentionally don't cache this result because we don't know whether + // the slot is public or private (since CLOAD can read both), and caching with the wrong domain flag + // could cause incorrect behavior in subsequent store operations. AssemblyItem item(Instruction::CLOAD, std::move(_debugData)); - Id value = m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); - m_storageContent[_slot] = {value, true}; - return value; + return m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); } KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, langutil::DebugData::ConstPtr _debugData) diff --git a/libevmasm/KnownState.h b/libevmasm/KnownState.h index 4847c9b30..47be4d38f 100644 --- a/libevmasm/KnownState.h +++ b/libevmasm/KnownState.h @@ -84,7 +84,7 @@ class KnownState using Id = ExpressionClasses::Id; struct StoreOperation { - enum Target { Invalid, Memory, Storage }; + enum Target { Invalid, Memory, Storage, ShieldedStorage }; bool isValid() const { return target != Invalid; } diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index 0fa74dbda..547b955ae 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -235,9 +235,9 @@ std::vector UnusedStoreEliminator::operationsF std::vector result; // Unknown read is worse than unknown write. if (sideEffects.memory != SideEffects::Effect::None) - result.emplace_back(Operation{Location::Memory, Effect::Read, {}, {}}); + result.emplace_back(Operation{Location::Memory, Effect::Read, {}, {}, false}); if (sideEffects.storage != SideEffects::Effect::None) - result.emplace_back(Operation{Location::Storage, Effect::Read, {}, {}}); + result.emplace_back(Operation{Location::Storage, Effect::Read, {}, {}, false}); return result; } @@ -249,7 +249,7 @@ std::vector UnusedStoreEliminator::operationsF { yulAssert(!(_op.lengthParameter && _op.lengthConstant)); yulAssert(_op.effect != Effect::None); - Operation ourOp{_op.location, _op.effect, {}, {}}; + Operation ourOp{_op.location, _op.effect, {}, {}, false}; if (_op.startParameter) ourOp.start = identifierNameIfSSA(_functionCall.arguments.at(*_op.startParameter)); if (_op.lengthParameter) @@ -261,6 +261,9 @@ std::vector UnusedStoreEliminator::operationsF case 32: ourOp.length = u256(32); break; default: yulAssert(false); } + // Mark shielded/private storage operations + if (_op.location == Location::Storage) + ourOp.isShieldedStorage = (*instruction == Instruction::CSTORE || *instruction == Instruction::CLOAD); return ourOp; } ); @@ -287,6 +290,12 @@ void UnusedStoreEliminator::applyOperation(UnusedStoreEliminator::Operation cons else if (_operation.effect == Effect::Write && knownCovered(storeOperation, _operation)) // This store is overwritten before being read, remove it from the active set. it = active.erase(it); + else if (_operation.effect == Effect::Write && hasStorageDomainConflict(storeOperation, _operation)) + { + // Storage domain conflict: mark the first store as used since the second will cause runtime error. + m_usedStores.insert(statement); + it = active.erase(it); + } else ++it; } @@ -301,6 +310,15 @@ bool UnusedStoreEliminator::knownUnrelated( return true; if (_op1.location == Location::Storage) { + // Different storage domains (public vs shielded) are generally unrelated. + // However, CLOAD can read BOTH domains, so it's related to any storage store. + // Note: _op1 is always a store (from m_storeOperations), so we only check _op2. + if (_op1.isShieldedStorage != _op2.isShieldedStorage) + { + bool op2IsCload = (_op2.effect == Effect::Read && _op2.isShieldedStorage); + if (!op2IsCload) + return true; + } if (_op1.start && _op2.start) { yulAssert( @@ -369,6 +387,10 @@ bool UnusedStoreEliminator::knownCovered( { if (_covered.location != _covering.location) return false; + // CSTORE/CLOAD and SSTORE/SLOAD operate on different storage domains. + // A public storage write (SSTORE) cannot cover a private storage write (CSTORE) and vice versa. + if (_covered.location == Location::Storage && _covered.isShieldedStorage != _covering.isShieldedStorage) + return false; if ( (_covered.start && _covered.start == _covering.start) && (_covered.length && _covered.length == _covering.length) @@ -406,6 +428,44 @@ bool UnusedStoreEliminator::knownCovered( return false; } +/// Detects a storage domain conflict between two operations. +/// +/// A storage domain conflict occurs when two operations (CSTORE/CLOAD vs SSTORE/SLOAD) +/// target the same storage slot but operate on different storage domains: +/// - Public storage domain: SSTORE/SLOAD operations +/// - Shielded/private storage domain: CSTORE/CLOAD operations +/// +/// These domains are separate and mutually exclusive. Attempting to access the same slot +/// with operations from different domains (e.g., CSTORE followed by SSTORE on slot 0) +/// will cause a runtime error, as the slot's privacy cannot be changed without first +/// clearing it. +/// +/// When such a conflict is detected, the optimizer must preserve both operations to +/// allow the runtime error to occur, rather than incorrectly optimizing away one of them. +/// +/// @param _op1 First operation to check +/// @param _op2 Second operation to check +/// @return true if the operations target the same slot in different storage domains +bool UnusedStoreEliminator::hasStorageDomainConflict( + UnusedStoreEliminator::Operation const& _op1, + UnusedStoreEliminator::Operation const& _op2 +) const +{ + // Only applies to storage operations + if (_op1.location != Location::Storage || _op2.location != Location::Storage) + return false; + + // Check if they operate on different storage domains (public vs shielded) + if (_op1.isShieldedStorage == _op2.isShieldedStorage) + return false; + + // Check if they target the same slot (or we can't prove they're different) + if (!_op1.start || !_op2.start) + return false; + + return !m_knowledgeBase.knownToBeDifferent(*_op1.start, *_op2.start); +} + void UnusedStoreEliminator::markActiveAsUsed( std::optional _onlyLocation ) diff --git a/libyul/optimiser/UnusedStoreEliminator.h b/libyul/optimiser/UnusedStoreEliminator.h index 45d363dfc..79b5067b1 100644 --- a/libyul/optimiser/UnusedStoreEliminator.h +++ b/libyul/optimiser/UnusedStoreEliminator.h @@ -88,6 +88,9 @@ class UnusedStoreEliminator: public UnusedStoreBase /// Length of affected area, unknown if not provided. /// Unused for storage. std::optional length; + /// Whether this is a shielded/private storage operation (CSTORE/CLOAD). + /// Only meaningful when location == Storage. + bool isShieldedStorage = false; }; private: @@ -115,6 +118,9 @@ class UnusedStoreEliminator: public UnusedStoreBase void applyOperation(Operation const& _operation); bool knownUnrelated(Operation const& _op1, Operation const& _op2) const; bool knownCovered(Operation const& _covered, Operation const& _covering) const; + /// Returns true if two operations target the same storage slot but different domains (public vs shielded). + /// This will cause a runtime error, so both operations must be preserved. + bool hasStorageDomainConflict(Operation const& _op1, Operation const& _op2) const; void markActiveAsUsed(std::optional _onlyLocation = std::nullopt); void clearActive(std::optional _onlyLocation = std::nullopt); diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 9b1fc1090..705a789df 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -2521,7 +2521,7 @@ BOOST_AUTO_TEST_CASE(inliner_invalid) } -BOOST_AUTO_TEST_CASE(cse_sstore_then_cload_no_optimization) +BOOST_AUTO_TEST_CASE(cse_sstore_then_cload_can_optimize) { AssemblyItems input{ u256(0x42), @@ -2530,14 +2530,13 @@ BOOST_AUTO_TEST_CASE(cse_sstore_then_cload_no_optimization) u256(0), Instruction::CLOAD }; - // Optimizer does stack manipulation but keeps CLOAD (doesn't replace with SSTORE value) + // CLOAD can read from public storage, so optimizer eliminates CLOAD and keeps 0x42 on stack checkCSE(input, { u256(0x42), u256(0), - Instruction::SWAP1, Instruction::DUP2, - Instruction::SSTORE, - Instruction::CLOAD + Instruction::SWAP1, + Instruction::SSTORE }); } diff --git a/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sstore_should_fail.sol b/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sstore_should_fail.sol new file mode 100644 index 000000000..2d856a62f --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sstore_should_fail.sol @@ -0,0 +1,16 @@ +contract C { + function test() external { + assembly { + cstore(0, 1) + } + // We put the following in a separate assembly block since the TypeChecker prevents + // mixing sstore and cstore in the same block. Eventually our typechecker should get smarter + // and also prevent mixing them across blocks when they access the same slot, but for now we + // at least make sure that SSTORE results in runtime failure. + assembly { + sstore(0, 0x1337) + } + } +} +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/semanticTests/inlineAssembly/sstore_zero_then_cstore.sol b/test/libsolidity/semanticTests/inlineAssembly/sstore_zero_then_cstore.sol new file mode 100644 index 000000000..d2fda9a14 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/sstore_zero_then_cstore.sol @@ -0,0 +1,21 @@ +contract C { + function test() external returns (uint256) { + assembly { + sstore(5, 0) // Clear slot 5 (public, value = 0) + cstore(5, 100) // Claim slot 5 for private use + } + // We put the following in a separate assembly block since the TypeChecker prevents + // mixing sstore and cstore in the same block. Eventually our typechecker should get smarter + // and also prevent mixing them across blocks when they access the same slot, + // but for now we use this test to test the CSE eliminator and make sure SLOAD is left as is + // to fail at runtime. + assembly { + let x := sload(5) // Should FAIL - slot is now private + // Below is only there to make sure sload is not dropped as dead code + mstore(0, x) + return(0, 32) + } + } +} +// ---- +// test() -> FAILURE From 798e2fd94a3eab7ca17dc40b91ff8ee87c5c2ba4 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:39:32 -0500 Subject: [PATCH 33/73] fix(yul-optimiser): dont hoist sloads since they can revert (#151) Fixes https://app.audithub.dev/app/organizations/224/projects/585/project-viewer?issueId=767 Made SLOAD be movableExceptForSideEffects so that the yul optimiser won't hoist it out of for loops (since it would be wrong in cases where the loop might run 0 times, but hoisting it could cause a revert). Companion to https://github.com/SeismicSystems/seismic-solidity/pull/148. ## Gas costs effect This is actually potentially very bad for certain contracts... see some of the tests that get changed with a lot of function calls not getting hoisted anymore. That being said.. the tests do seem a bit artificial. --- libevmasm/SemanticInformation.cpp | 5 +++- test/libyul/functionSideEffects/storage.yul | 2 +- .../loopInvariantCodeMotion/move_cload.yul | 25 +++++++++++++++++ .../move_memory_function.yul | 11 +++++--- .../move_state_function.yul | 9 ++++--- .../move_storage_function.yul | 5 ++-- .../no_move_sload_can_revert.yul | 27 +++++++++++++++++++ .../simple_storage.yul | 4 ++- 8 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_cload.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_sload_can_revert.yul diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 684b4453f..f6e97b290 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -546,7 +546,10 @@ bool SemanticInformation::movableApartFromEffects(Instruction _instruction) case Instruction::RETURNDATASIZE: case Instruction::BALANCE: case Instruction::SELFBALANCE: - case Instruction::SLOAD: + // SLOAD is NOT movable apart from effects because it can revert due to + // confidentiality mismatch (reading private storage). + // Hoisting SLOAD out of a loop could change behavior from non-reverting + // to reverting for zero-iteration loops. case Instruction::CLOAD: case Instruction::TLOAD: case Instruction::KECCAK256: diff --git a/test/libyul/functionSideEffects/storage.yul b/test/libyul/functionSideEffects/storage.yul index 3d1e2d836..eb705ccfe 100644 --- a/test/libyul/functionSideEffects/storage.yul +++ b/test/libyul/functionSideEffects/storage.yul @@ -11,4 +11,4 @@ // a: writes storage // f: writes storage // g: writes other state, writes storage, writes memory -// h: movable apart from effects, can be removed, can be removed if no msize, reads storage +// h: can be removed, can be removed if no msize, reads storage diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_cload.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_cload.yul new file mode 100644 index 000000000..71fabd027 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_cload.yul @@ -0,0 +1,25 @@ +{ + let b := 1 + // CLOAD CAN be hoisted because it cannot revert (in Seismic's updated semantics). + // Unlike SLOAD, CLOAD can read both public and private slots. + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := cload(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := cload(mul(inv, 3)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul index f8c18f5f4..9423d33d7 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul @@ -1,4 +1,6 @@ { + // In Seismic, SLOAD is marked as having effects because it can revert. + // Therefore, it cannot be hoisted out of loops. function g() -> x { x := add(sload(mload(x)), 1) } let b := 1 @@ -14,11 +16,12 @@ // { // let b := 1 // let a := 1 -// let t := mload(g()) -// let s := keccak256(g(), 32) -// let q := g() // for { } iszero(eq(a, 10)) { a := add(a, 1) } -// { } +// { +// let t := mload(g()) +// let s := keccak256(g(), 32) +// let q := g() +// } // function g() -> x // { x := add(sload(mload(x)), 1) } // } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul index a6ccbb39f..390c12a34 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul @@ -1,5 +1,7 @@ { function f() -> x { x := mload(g()) } + // In Seismic, SLOAD is marked as having effects because it can revert. + // Therefore, it cannot be hoisted out of loops. function g() -> x { x := add(sload(x), 1) } let b := 1 @@ -14,10 +16,11 @@ // { // let b := 1 // let a := 1 -// let t := balance(f()) -// let q := g() // for { } iszero(eq(a, 10)) { a := add(a, 1) } -// { } +// { +// let t := balance(f()) +// let q := g() +// } // function f() -> x // { x := mload(g()) } // function g() -> x_1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul index 230e13d14..b91668b95 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul @@ -4,6 +4,8 @@ let b := 1 for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + // In Seismic, SLOAD is marked as having effects because it can revert. + // Therefore, it cannot be hoisted out of loops. let t := sload(f()) let q := g() } @@ -14,10 +16,9 @@ // { // let b := 1 // let a := 1 -// let t := sload(f()) // let q := g() // for { } iszero(eq(a, 10)) { a := add(a, 1) } -// { } +// { let t := sload(f()) } // function f() -> x // { x := g() } // function g() -> x_1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_sload_can_revert.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_sload_can_revert.yul new file mode 100644 index 000000000..ff628618c --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_sload_can_revert.yul @@ -0,0 +1,27 @@ +{ + let b := 1 + // SLOAD should NOT be hoisted even though there's no storage write in the loop. + // In Seismic, SLOAD can revert due to confidentiality mismatch. + // Hoisting it out of a zero-iteration loop would change behavior from + // non-reverting to reverting. + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let x := sload(mul(inv, 3)) +// a := add(x, 1) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul index 022d66b2d..5bf2843c3 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul @@ -2,6 +2,8 @@ let b := 1 for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { let inv := add(b, 42) + // In Seismic, SLOAD is marked as having effects because it can revert. + // Therefore, it cannot be hoisted out of loops. let x := sload(mul(inv, 3)) a := add(x, 1) mstore(a, inv) @@ -14,9 +16,9 @@ // let b := 1 // let a := 1 // let inv := add(b, 42) -// let x := sload(mul(inv, 3)) // for { } iszero(eq(a, 10)) { a := add(a, 1) } // { +// let x := sload(mul(inv, 3)) // a := add(x, 1) // mstore(a, inv) // } From 5f21e45a7031fc2635f3de2e69467d197268ae06 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:54:41 -0500 Subject: [PATCH 34/73] fix(evmasm-optimiser): mark sload as having effects (#148) Fixes https://app.audithub.dev/app/organizations/224/projects/585/project-viewer?issueId=763 and https://app.audithub.dev/app/organizations/224/projects/585/project-viewer?issueId=772 Companion to https://github.com/SeismicSystems/seismic-solidity/pull/151 SLOADs can revert in seismic-solidity when trying to read a confidential slot. We thus mark it as having effects so that SLOADs that could revert at runtime are not removed by optimizers (peephole, cse, etc). Added a large comment in Instruction.cpp to explain the tradeoff here, and the fact that we might want to take a more fine-grained approach if we find the gas costs unnaceptable. ## Why this is important Here's an example brought up by Benjamin Sepanski from veridise: > For example, the UnusedPruner could remove a trailing SLOAD which is intended to be used by a user to force a revert in case the data is confidential ## Tests Had to "fix" a bunch of the tests. Made separate commits for the different types of tests, so worth looking at individual commits for the different things that need to change because of this coarse grained approach (making SLOAD have side effects instead of just fixing the optimisers directly to keep certain optimizations possible). I use "fix" in quotes because we are basically getting rid of many optimizations because of making SLOAD have side effects. We might want to benchmark against some set of contracts to see whether this is acceptable or not before pulling the trigger. Otherwise we might need to implement a more fine-grained solution. I think the main issue this might cause is that the optimizer can no longer transform "SLOAD(x) SLOAD(x)" into "SLOAD(x) DUP", so we incur an extra SLOAD when loading from the same slot. Although maybe not that bad since second is a warm access which is only 100 gas..? --- libevmasm/Instruction.cpp | 9 ++- test/libevmasm/Optimiser.cpp | 76 +++++++++++++++++-- test/libsolidity/gasTests/abiv2_optimised.sol | 6 +- .../gasTests/dispatch_large_optimised.sol | 42 +++++----- .../gasTests/dispatch_medium_optimised.sol | 18 ++--- .../gasTests/dispatch_small_optimised.sol | 8 +- test/libsolidity/gasTests/storage_costs.sol | 8 +- test/libyul/functionSideEffects/storage.yul | 4 +- test/libyul/objectCompiler/verbatim_bug.yul | 6 +- .../fullSuite/and_or_combination.yul | 7 +- .../unusedFunctionParameterPruner.yul | 7 +- .../unusedFunctionParameterPruner_return.yul | 7 +- 12 files changed, 146 insertions(+), 52 deletions(-) diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index e3fc57065..72aebd2c4 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -259,7 +259,14 @@ static std::map const c_instructionInfo = {Instruction::MLOAD, {"MLOAD", 0, 1, 1, true, Tier::VeryLow}}, {Instruction::MSTORE, {"MSTORE", 0, 2, 0, true, Tier::VeryLow}}, {Instruction::MSTORE8, {"MSTORE8", 0, 2, 0, true, Tier::VeryLow}}, - {Instruction::SLOAD, {"SLOAD", 0, 1, 1, false, Tier::Special}}, + // We mark SLOAD as having side effects because SLOAD can potentially revert when accessing confidential storage. + // This is to make sure that optimizers (peephole, CSE, etc) do not move or eliminate SLOAD instructions incorrectly. + // Note that this is a VERY conservative approach, that can lead to much less optimal code. + // For example afaiu it would prevent 2 SLOADs of the same storage slot from becoming a single SLOAD followed by a DUP. + // In theory, reverts are very different from general side effects, since they abort execution rather than modifying state. + // Notice that DIV, MOD, SDIV, etc. are not marked as having side effects, even though they can revert on division by zero. + // If we benchmark and find that this conservative approach has too high a cost, we should consider more refined approaches. + {Instruction::SLOAD, {"SLOAD", 0, 1, 1, true, Tier::Special}}, {Instruction::SSTORE, {"SSTORE", 0, 2, 0, true, Tier::Special}}, {Instruction::TLOAD, {"TLOAD", 0, 1, 1, false, Tier::WarmAccess}}, {Instruction::TSTORE, {"TSTORE", 0, 2, 0, true, Tier::WarmAccess}}, diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 705a789df..4cc686fc3 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -194,17 +194,16 @@ BOOST_AUTO_TEST_CASE(cse_assign_immutable_breaks) BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.begin() + 2); } +// SEISMIC NOTE: In Seismic Solidity, SLOAD has side effects (can revert on confidential storage access). +// CSE stops at SLOAD, so we use fullCSE and just check output is not empty. BOOST_AUTO_TEST_CASE(cse_intermediate_swap) { - evmasm::KnownState state; - evmasm::CommonSubexpressionEliminator cse(state); AssemblyItems input{ Instruction::SWAP1, Instruction::POP, Instruction::ADD, u256(0), Instruction::SWAP1, Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1, Instruction::DIV, u256(0xff), Instruction::AND }; - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.end()); - AssemblyItems output = cse.getOptimizedItems(); + AssemblyItems output = fullCSE(input); BOOST_CHECK(!output.empty()); } @@ -391,6 +390,7 @@ BOOST_AUTO_TEST_CASE(cse_storage) u256(0), Instruction::SSTORE }; + /* Upstream code does this checkCSE(input, { u256(0), Instruction::DUP1, @@ -400,6 +400,20 @@ BOOST_AUTO_TEST_CASE(cse_storage) Instruction::SWAP1, Instruction::SSTORE }); + */ + + // With Seismic Solidity SLOAD having side effects, CSE cannot optimize across SLOADs, + // so we use checkFullCSE which handles CSE stopping at side-effect instructions, + // to at least do constant folding. + checkFullCSE(input, { + u256(0), + Instruction::SLOAD, + u256(0), + Instruction::SLOAD, + Instruction::ADD, + u256(0), + Instruction::SSTORE + }); } BOOST_AUTO_TEST_CASE(shielded_cse_storage) { @@ -437,12 +451,17 @@ BOOST_AUTO_TEST_CASE(cse_noninterleaved_storage) Instruction::DUP3, Instruction::SSTORE }; + /* Upstream code does this checkCSE(input, { u256(8), Instruction::DUP2, Instruction::SSTORE, u256(7) }); + */ + + // With SLOAD having side effects, CSE cannot optimize across it + checkFullCSE(input, input); } BOOST_AUTO_TEST_CASE(shielded_cse_noninterleaved_storage) @@ -480,7 +499,11 @@ BOOST_AUTO_TEST_CASE(cse_interleaved_storage) Instruction::DUP3, Instruction::SSTORE // store different value to "DUP1" }; + /* Upstream code does this checkCSE(input, input); + */ + // Seismic Solidity requires FullCSE since SLOAD has side effects + checkFullCSE(input, input); } BOOST_AUTO_TEST_CASE(shielded_cse_interleaved_storage) @@ -515,6 +538,7 @@ BOOST_AUTO_TEST_CASE(cse_interleaved_storage_same_value) Instruction::DUP3, Instruction::SSTORE // store same value to "DUP1" }; + /* Upstream code does this checkCSE(input, { u256(7), Instruction::DUP2, @@ -522,6 +546,20 @@ BOOST_AUTO_TEST_CASE(cse_interleaved_storage_same_value) Instruction::DUP2, Instruction::SLOAD }); + */ + + // With SLOAD having side effects, CSE cannot optimize across it. + // But constant folding still happens (6 + 1 = 7). + checkFullCSE(input, { + u256(7), + Instruction::DUP2, + Instruction::SSTORE, + Instruction::DUP2, + Instruction::SLOAD, + u256(7), + Instruction::DUP3, + Instruction::SSTORE + }); } BOOST_AUTO_TEST_CASE(shielded_cse_interleaved_storage_same_value) @@ -563,6 +601,7 @@ BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location) u256(1), Instruction::SSTORE // store different value at 1 }; + /* Upstream code does this checkCSE(input, { u256(2), Instruction::SLOAD, @@ -570,6 +609,10 @@ BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location) u256(1), Instruction::SSTORE }); + */ + + // With SLOAD having side effects, CSE cannot optimize across it + checkFullCSE(input, input); } BOOST_AUTO_TEST_CASE(shielded_cse_interleaved_storage_at_known_location) @@ -615,6 +658,7 @@ BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location_offset) Instruction::ADD, Instruction::SSTORE // store different value at "DUP1"+1 }; + /* Upstream code does this checkCSE(input, { u256(2), Instruction::DUP2, @@ -626,6 +670,10 @@ BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location_offset) Instruction::ADD, Instruction::SSTORE }); + */ + + // With SLOAD having side effects, CSE cannot optimize across it + checkFullCSE(input, input); } BOOST_AUTO_TEST_CASE(shielded_cse_interleaved_storage_at_known_location_offset) @@ -905,6 +953,10 @@ BOOST_AUTO_TEST_CASE(cse_equality_on_initially_known_stack) BOOST_CHECK(find(output.begin(), output.end(), AssemblyItem(u256(1))) != output.end()); } +// Commenting out this test, but keeping here for documenting how seismic solidity semantics +// differ from normal solidity. Because SLOAD has side effects, StackTooDeepException on +// sequence access simply can't occur anymore because CSE stops at SLOAD before +// it can try to access previous sequences. BOOST_AUTO_TEST_CASE(cse_access_previous_sequence) { // Tests that the code generator detects whether it tries to access SLOAD instructions @@ -924,7 +976,7 @@ BOOST_AUTO_TEST_CASE(cse_access_previous_sequence) u256(0), Instruction::SLOAD, }; - BOOST_CHECK_THROW(CSE(input, state), StackTooDeepException); + // BOOST_CHECK_THROW(CSE(input, state), StackTooDeepException); // @todo for now, this throws an exception, but it should recover to the following // (or an even better version) at some point: // 0, SLOAD, 1, ADD, SSTORE, 0 SLOAD @@ -1881,6 +1933,11 @@ BOOST_AUTO_TEST_CASE(cse_verbatim_mload) checkFullCSE(input, input); } +// SEISMIC NOTE: In Seismic Solidity, SLOAD has side effects (can revert on confidential storage access). +// Therefore two SLOADs from the same slot cannot be combined into one SLOAD + DUP. +// Note that this is generally not true since the only revert possible from an SLOAD is from attempting +// to read from a confidential slot, so reading the same slot twice should be optimizable. +// However we have not implemented such an analysis yet, so we conservatively assume SLOAD has generic side effects. BOOST_AUTO_TEST_CASE(cse_sload_verbatim_dup) { auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0}; @@ -1892,6 +1949,7 @@ BOOST_AUTO_TEST_CASE(cse_sload_verbatim_dup) verbatim }; + /* Upstream code does this AssemblyItems output{ u256(0), Instruction::SLOAD, @@ -1901,6 +1959,9 @@ BOOST_AUTO_TEST_CASE(cse_sload_verbatim_dup) checkCSE(input, output); checkFullCSE(input, output); + */ + // With SLOAD having side effects, the two SLOADs cannot be combined + checkFullCSE(input, input); } BOOST_AUTO_TEST_CASE(shielded_cse_sload_verbatim_dup) @@ -2593,6 +2654,7 @@ BOOST_AUTO_TEST_CASE(cse_sstore_sload_same_slot_can_optimize) u256(0), Instruction::SLOAD }; + /* Upstream code does this // Optimizer eliminates SLOAD and keeps value on stack checkCSE(input, { u256(0x42), @@ -2601,6 +2663,10 @@ BOOST_AUTO_TEST_CASE(cse_sstore_sload_same_slot_can_optimize) Instruction::SWAP1, Instruction::SSTORE }); + */ + + // In Seismic Solidity, with SLOAD having side effects, it cannot be eliminated + checkFullCSE(input, input); } BOOST_AUTO_TEST_CASE(cse_cstore_cload_same_slot_can_optimize) diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index e23a7bff2..748643a08 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -19,11 +19,11 @@ contract C { // optimize-yul: true // ---- // creation: -// codeDepositCost: 618200 +// codeDepositCost: 619200 // executionCost: 649 -// totalCost: 618849 +// totalCost: 619849 // external: -// a(): 2283 +// a(): 2295 // b(uint256): 4649 // f1(uint256): 304 // f2(uint256[],string[],uint16,address): infinite diff --git a/test/libsolidity/gasTests/dispatch_large_optimised.sol b/test/libsolidity/gasTests/dispatch_large_optimised.sol index 5ab62e892..4c7ca9567 100644 --- a/test/libsolidity/gasTests/dispatch_large_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_large_optimised.sol @@ -29,29 +29,29 @@ contract Large { // optimize-runs: 2 // ---- // creation: -// codeDepositCost: 224600 +// codeDepositCost: 225800 // executionCost: 267 -// totalCost: 224867 +// totalCost: 226067 // external: -// a(): 2281 +// a(): 2293 // b(uint256): 4934 // f0(uint256): 363 -// f1(uint256): 47002 -// f2(uint256): 24967 -// f3(uint256): 25055 -// f4(uint256): 25033 -// f5(uint256): 25011 -// f6(uint256): 24923 -// f7(uint256): 24703 -// f8(uint256): 24835 -// f9(uint256): 24857 +// f1(uint256): 47005 +// f2(uint256): 24970 +// f3(uint256): 25058 +// f4(uint256): 25036 +// f5(uint256): 25014 +// f6(uint256): 24926 +// f7(uint256): 24706 +// f8(uint256): 24838 +// f9(uint256): 24860 // g0(uint256): 603 -// g1(uint256): 46714 -// g2(uint256): 24701 -// g3(uint256): 24789 -// g4(uint256): 24767 -// g5(uint256): 24855 -// g6(uint256): 24635 -// g7(uint256): 24745 -// g8(uint256): 24723 -// g9(uint256): 24569 +// g1(uint256): 46717 +// g2(uint256): 24704 +// g3(uint256): 24792 +// g4(uint256): 24770 +// g5(uint256): 24858 +// g6(uint256): 24638 +// g7(uint256): 24748 +// g8(uint256): 24726 +// g9(uint256): 24572 diff --git a/test/libsolidity/gasTests/dispatch_medium_optimised.sol b/test/libsolidity/gasTests/dispatch_medium_optimised.sol index 2ea33955e..cff9a5197 100644 --- a/test/libsolidity/gasTests/dispatch_medium_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_medium_optimised.sol @@ -17,16 +17,16 @@ contract Medium { // optimize-runs: 2 // ---- // creation: -// codeDepositCost: 126000 +// codeDepositCost: 127200 // executionCost: 169 -// totalCost: 126169 +// totalCost: 127369 // external: -// a(): 2281 +// a(): 2293 // b(uint256): 4692 -// f1(uint256): 46782 -// f2(uint256): 24725 -// f3(uint256): 24769 +// f1(uint256): 46785 +// f2(uint256): 24728 +// f3(uint256): 24772 // g0(uint256): 361 -// g7(uint256): 24635 -// g8(uint256): 24613 -// g9(uint256): 24569 +// g7(uint256): 24638 +// g8(uint256): 24616 +// g9(uint256): 24572 diff --git a/test/libsolidity/gasTests/dispatch_small_optimised.sol b/test/libsolidity/gasTests/dispatch_small_optimised.sol index b59bdafc2..6a8efaca8 100644 --- a/test/libsolidity/gasTests/dispatch_small_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_small_optimised.sol @@ -11,11 +11,11 @@ contract Small { // bytecodeFormat: legacy // ---- // creation: -// codeDepositCost: 58200 +// codeDepositCost: 59400 // executionCost: 109 -// totalCost: 58309 +// totalCost: 59509 // external: // fallback: 117 -// a(): 2259 +// a(): 2271 // b(uint256): 4582 -// f1(uint256): 46716 +// f1(uint256): 46719 diff --git a/test/libsolidity/gasTests/storage_costs.sol b/test/libsolidity/gasTests/storage_costs.sol index 871c15eab..974227c3d 100644 --- a/test/libsolidity/gasTests/storage_costs.sol +++ b/test/libsolidity/gasTests/storage_costs.sol @@ -17,10 +17,10 @@ contract C { // bytecodeFormat: legacy // ---- // creation: -// codeDepositCost: 25600 -// executionCost: 73 -// totalCost: 25673 +// codeDepositCost: 28000 +// executionCost: 79 +// totalCost: 28079 // external: -// readX(): 2288 +// readX(): 2322 // resetX(): 5114 // setX(uint256): 22309 diff --git a/test/libyul/functionSideEffects/storage.yul b/test/libyul/functionSideEffects/storage.yul index eb705ccfe..c6ad73897 100644 --- a/test/libyul/functionSideEffects/storage.yul +++ b/test/libyul/functionSideEffects/storage.yul @@ -1,3 +1,5 @@ +// SEISMIC NOTE: In Seismic Solidity, SLOAD has side effects (can revert on confidential storage access). +// Therefore function h() which calls sload(0) can no longer be marked as "can be removed". { function a() { sstore(0, 1) } function f() { a() } @@ -11,4 +13,4 @@ // a: writes storage // f: writes storage // g: writes other state, writes storage, writes memory -// h: can be removed, can be removed if no msize, reads storage +// h: reads storage diff --git a/test/libyul/objectCompiler/verbatim_bug.yul b/test/libyul/objectCompiler/verbatim_bug.yul index 892ec13fa..e447bc879 100644 --- a/test/libyul/objectCompiler/verbatim_bug.yul +++ b/test/libyul/objectCompiler/verbatim_bug.yul @@ -1,3 +1,5 @@ +// SEISMIC NOTE: In Seismic Solidity, SLOAD has side effects (can revert on confidential storage access). +// The optimizer can no longer do "dup1; sload" to reuse the 0 value. Instead it does "sload; 0x00; swap1". object "a" { code { let dummy := 0xAABBCCDDEEFF @@ -32,8 +34,10 @@ object "a" { // /* "source":65:66 */ // 0x00 // /* "source":59:67 */ -// dup1 // sload +// /* "source":94:95 */ +// 0x00 +// swap1 // /* "source":133:225 */ // dup1 // iszero diff --git a/test/libyul/yulOptimizerTests/fullSuite/and_or_combination.yul b/test/libyul/yulOptimizerTests/fullSuite/and_or_combination.yul index b5f456054..de5e7bfc0 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/and_or_combination.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/and_or_combination.yul @@ -1,3 +1,6 @@ +// SEISMIC NOTE: In Seismic Solidity, SLOAD has side effects (can revert on confidential storage access). +// Therefore sload(a) cannot be eliminated even though its result (x) is ultimately unused after optimization. +// The optimizer preserves it as pop(sload(a)) to execute the side effect while discarding the result. { // Tests that masks that "add" up to // the full bit width are removed. @@ -20,6 +23,8 @@ // // { // { -// sstore(sload(0), 0xad9c000000000000823500000000000056ce0000000000002b67) +// let a := sload(0) +// pop(sload(a)) +// sstore(a, 0xad9c000000000000823500000000000056ce0000000000002b67) // } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul index 9d893a7d3..7f65bb3c5 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul @@ -1,3 +1,6 @@ +// SEISMIC NOTE: In Seismic Solidity, SLOAD has side effects (can revert on confidential storage access). +// Therefore sload(0) cannot be eliminated even though its result is passed to an unused parameter. +// The optimizer preserves it as pop(sload(0)) to execute the side effect while discarding the result. { let x, y := foo(sload(0),sload(32)) sstore(0, x) @@ -19,7 +22,9 @@ // // { // { -// let out1, out2 := foo(sload(32)) +// let _1 := sload(32) +// pop(sload(0)) +// let out1, out2 := foo(_1) // sstore(0, out2) // let out1_1, out2_1 := foo(sload(8)) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul index f95e6f30d..5e0c8f2c3 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul @@ -1,3 +1,6 @@ +// SEISMIC NOTE: In Seismic Solidity, SLOAD has side effects (can revert on confidential storage access). +// Therefore sload(0) cannot be eliminated even though its result is passed to an unused parameter. +// The optimizer preserves it as pop(sload(0)) to execute the side effect while discarding the result. { let x, y, z := foo(sload(0),sload(32)) sstore(0, x) @@ -23,7 +26,9 @@ // // { // { -// let out1, out2 := foo(sload(32)) +// let _1 := sload(32) +// pop(sload(0)) +// let out1, out2 := foo(_1) // sstore(0, 0) // let out1_1, out2_1 := foo(sload(8)) // } From 62bccfa6b9b390ed1d59c876f0e534691f5574ff Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 09:03:41 -0500 Subject: [PATCH 35/73] test(yul): add tests for timestampms identifier reservation Add syntax tests to verify that: - cstore, cload, and timestampms can be used as identifiers pre-Mercury - timestampms is reserved as a builtin post-Mercury The pre-Mercury test currently fails because createReservedIdentifiers() does not include TIMESTAMPMS in the shieldeStorageException check. --- ...cstore_cload_timestampms_as_identifiers_pre_mercury.yul | 7 +++++++ .../timestampms_as_identifier_post_mercury.yul | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 test/libyul/yulSyntaxTests/cstore_cload_timestampms_as_identifiers_pre_mercury.yul create mode 100644 test/libyul/yulSyntaxTests/timestampms_as_identifier_post_mercury.yul diff --git a/test/libyul/yulSyntaxTests/cstore_cload_timestampms_as_identifiers_pre_mercury.yul b/test/libyul/yulSyntaxTests/cstore_cload_timestampms_as_identifiers_pre_mercury.yul new file mode 100644 index 000000000..395f7f85f --- /dev/null +++ b/test/libyul/yulSyntaxTests/cstore_cload_timestampms_as_identifiers_pre_mercury.yul @@ -0,0 +1,7 @@ +{ + function cstore() {} + function cload() {} + function timestampms() {} +} +// ==== +// EVMVersion: =mercury +// ---- +// ParserError 5568: (15-26): Cannot use builtin function name "timestampms" as identifier name. From bc34366346a1a418c4ec4527d1e54070c59a3be5 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 09:03:50 -0500 Subject: [PATCH 36/73] fix(yul): add timestampms to reserved identifier exceptions for pre-Mercury The shieldeStorageException in createReservedIdentifiers() excluded CSTORE and CLOAD from reserved identifiers for pre-Mercury EVM versions, but not TIMESTAMPMS. This caused an inconsistency where users couldn't define a function called timestampms() in Yul when targeting pre-Mercury EVMs, even though the opcode doesn't exist in those versions. Add TIMESTAMPMS to the exception check so all Mercury-only opcodes are handled consistently. --- libyul/backends/evm/EVMDialect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index b0a0c5878..a9eef4455 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -137,7 +137,7 @@ std::set> createReservedIdentifiers(langutil::EVMVersio { return _evmVersion < langutil::EVMVersion::mercury() && - (_instr == evmasm::Instruction::CSTORE || _instr == evmasm::Instruction::CLOAD); + (_instr == evmasm::Instruction::CSTORE || _instr == evmasm::Instruction::CLOAD || _instr == evmasm::Instruction::TIMESTAMPMS); }; auto eofIdentifiersException = [&](evmasm::Instruction _instr) -> bool From 5bc3ba70ec7eaedb5025b547038ae2ec5ea0087b Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 09:54:14 -0500 Subject: [PATCH 37/73] test(codegen): add tests for string literal to sbytes conversion Add semantic and syntax tests for explicit conversion from string and hex literals to shielded fixed bytes types (sbytesN). These tests expose the internal compiler error that occurs when converting literals like sbytes16("hello") - the compiler currently lacks handling for StringLiteral -> ShieldedFixedBytes conversions in both legacy and via-ir pipelines. --- .../sbytes_string_literal_conversion.sol | 38 +++++++++++++++++++ .../sbytes_string_literal_conversion.sol | 28 ++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 test/libsolidity/semanticTests/types/sbytes_string_literal_conversion.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_string_literal_conversion.sol diff --git a/test/libsolidity/semanticTests/types/sbytes_string_literal_conversion.sol b/test/libsolidity/semanticTests/types/sbytes_string_literal_conversion.sol new file mode 100644 index 000000000..64b09f9e0 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_string_literal_conversion.sol @@ -0,0 +1,38 @@ +contract C { + function testExplicitConversion() public pure returns (bool) { + // Test explicit conversion from string literal to sbytes + sbytes4 sb4 = sbytes4("abcd"); + require(sb4 == sbytes4(bytes4("abcd"))); + + sbytes8 sb8 = sbytes8("abcdefgh"); + require(sb8 == sbytes8(bytes8("abcdefgh"))); + + sbytes16 sb16 = sbytes16("abcdefghijklmnop"); + require(sb16 == sbytes16(bytes16("abcdefghijklmnop"))); + + return true; + } + + function testShortString() public pure returns (bool) { + // String literal shorter than target type (should be left-aligned, zero-padded) + sbytes4 sb4 = sbytes4("ab"); + require(sb4 == sbytes4(bytes4("ab"))); + + sbytes8 sb8 = sbytes8("ab"); + require(sb8 == sbytes8(bytes8("ab"))); + + return true; + } + + function testHexLiteral() public pure returns (bool) { + // Test explicit conversion from hex literal to sbytes + sbytes4 sb4 = sbytes4(hex"61626364"); + require(sb4 == sbytes4(bytes4("abcd"))); + + return true; + } +} +// ---- +// testExplicitConversion() -> true +// testShortString() -> true +// testHexLiteral() -> true diff --git a/test/libsolidity/syntaxTests/types/sbytes_string_literal_conversion.sol b/test/libsolidity/syntaxTests/types/sbytes_string_literal_conversion.sol new file mode 100644 index 000000000..7095131d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_string_literal_conversion.sol @@ -0,0 +1,28 @@ +contract C { + function test() internal pure { + // Test explicit conversion from string literal to sbytes + sbytes1 sb1 = sbytes1("a"); + sbytes4 sb4 = sbytes4("abcd"); + sbytes8 sb8 = sbytes8("abcdefgh"); + sbytes16 sb16 = sbytes16("abcdefghijklmnop"); + sbytes32 sb32 = sbytes32("abcdefghijklmnopqrstuvwxyz012345"); + + // Test explicit conversion from hex literal to sbytes + sbytes4 sb4_hex = sbytes4(hex"61626364"); + sbytes8 sb8_hex = sbytes8(hex"6162636465666768"); + + // Test with shorter string (left-aligned, zero-padded) + sbytes4 sb4_short = sbytes4("ab"); + sbytes8 sb8_short = sbytes8("ab"); + } +} +// ---- +// Warning 2072: (123-134): Unused local variable. +// Warning 2072: (159-170): Unused local variable. +// Warning 2072: (198-209): Unused local variable. +// Warning 2072: (241-254): Unused local variable. +// Warning 2072: (295-308): Unused local variable. +// Warning 2072: (429-444): Unused local variable. +// Warning 2072: (479-494): Unused local variable. +// Warning 2072: (602-619): Unused local variable. +// Warning 2072: (645-662): Unused local variable. From 0c8d7a264cf88309167639baeb39577f11aa61cb Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 09:54:36 -0500 Subject: [PATCH 38/73] fix(codegen): support string literal to ShieldedFixedBytes conversion Handle StringLiteral -> ShieldedFixedBytes in both legacy and via-ir codegen pipelines by extending the existing FixedBytes handling. In CompilerUtils::convertType (legacy) and YulUtilFunctions::conversionFunctionSpecial (via-ir), the condition for handling string literal to fixed bytes conversion now also checks for ShieldedFixedBytes. Since ShieldedFixedBytesType inherits from FixedBytesType, the existing numBytes() call and conversion logic work correctly for both types. This fixes the internal compiler error when using expressions like: sbytes16 x = sbytes16("hello"); sbytes32 y = sbytes32(hex"abcd"); --- libsolidity/codegen/CompilerUtils.cpp | 2 +- libsolidity/codegen/YulUtilFunctions.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 03eed5ac6..6fe394384 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -1007,7 +1007,7 @@ void CompilerUtils::convertType( auto const& literalType = dynamic_cast(_typeOnStack); std::string const& value = literalType.value(); bytesConstRef data(value); - if (targetTypeCategory == Type::Category::FixedBytes) + if (targetTypeCategory == Type::Category::FixedBytes || targetTypeCategory == Type::Category::ShieldedFixedBytes) { unsigned const numBytes = dynamic_cast(_targetType).numBytes(); solAssert(data.size() <= 32); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index c4252a5ce..bd3c2392b 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -4526,7 +4526,7 @@ std::string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type "Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented." ); std::string const& data = dynamic_cast(_from).value(); - if (_to.category() == Type::Category::FixedBytes) + if (_to.category() == Type::Category::FixedBytes || _to.category() == Type::Category::ShieldedFixedBytes) { unsigned const numBytes = dynamic_cast(_to).numBytes(); solAssert(data.size() <= 32, ""); From 37d3bd75e5664e6dd88542d53c1e74ab5e58f3c9 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 15:38:31 -0500 Subject: [PATCH 39/73] test(types): add tests for disallowed saddress member access Add syntax tests verifying that balance, call, delegatecall, staticcall, send, and transfer are not available on shielded addresses. Only code and codehash should be allowed. Error messages guide users to cast to address first. Update existing shielded_address_members test to remove balance access. --- .../address/shielded_address_allowed_members.sol | 12 ++++++++++++ .../types/address/shielded_address_members.sol | 1 - .../types/address/shielded_address_no_balance.sol | 8 ++++++++ .../types/address/shielded_address_no_call.sol | 8 ++++++++ .../address/shielded_address_no_delegatecall.sol | 8 ++++++++ .../types/address/shielded_address_no_send.sol | 8 ++++++++ .../types/address/shielded_address_no_staticcall.sol | 8 ++++++++ .../types/address/shielded_address_no_transfer.sol | 8 ++++++++ 8 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/types/address/shielded_address_allowed_members.sol create mode 100644 test/libsolidity/syntaxTests/types/address/shielded_address_no_balance.sol create mode 100644 test/libsolidity/syntaxTests/types/address/shielded_address_no_call.sol create mode 100644 test/libsolidity/syntaxTests/types/address/shielded_address_no_delegatecall.sol create mode 100644 test/libsolidity/syntaxTests/types/address/shielded_address_no_send.sol create mode 100644 test/libsolidity/syntaxTests/types/address/shielded_address_no_staticcall.sol create mode 100644 test/libsolidity/syntaxTests/types/address/shielded_address_no_transfer.sol diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_allowed_members.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_allowed_members.sol new file mode 100644 index 000000000..7fb4f58f3 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_allowed_members.sol @@ -0,0 +1,12 @@ +contract C { + saddress private a; + + function testCode() external view returns (bytes memory) { + return a.code; + } + + function testCodehash() external view returns (bytes32) { + return a.codehash; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_members.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_members.sol index ba36606e1..6be39762f 100644 --- a/test/libsolidity/syntaxTests/types/address/shielded_address_members.sol +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_members.sol @@ -1,6 +1,5 @@ contract C { function f() public view returns (saddress) { return saddress(this); } - function g() public view returns (uint) { return f().balance; } function h() public view returns (bytes memory) { return f().code; } function i() public view returns (uint) { return f().code.length; } function j() public view returns (uint) { return h().length; } diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_no_balance.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_no_balance.sol new file mode 100644 index 000000000..7b74abf20 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_no_balance.sol @@ -0,0 +1,8 @@ +contract C { + saddress a; + function f() external view returns (uint256) { + return a.balance; + } +} +// ---- +// TypeError 3125: (95-104): Member "balance" not found or not visible after argument-dependent lookup in saddress. Cast to address first: "address(a).balance". diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_no_call.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_no_call.sol new file mode 100644 index 000000000..48a60c72d --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_no_call.sol @@ -0,0 +1,8 @@ +contract C { + saddress a; + function f() external returns (bool, bytes memory) { + return a.call(""); + } +} +// ---- +// TypeError 3125: (101-107): Member "call" not found or not visible after argument-dependent lookup in saddress. Cast to address first: "address(a).call". diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_no_delegatecall.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_no_delegatecall.sol new file mode 100644 index 000000000..11e267ae6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_no_delegatecall.sol @@ -0,0 +1,8 @@ +contract C { + saddress a; + function f() external returns (bool, bytes memory) { + return a.delegatecall(""); + } +} +// ---- +// TypeError 3125: (101-115): Member "delegatecall" not found or not visible after argument-dependent lookup in saddress. Cast to address first: "address(a).delegatecall". diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_no_send.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_no_send.sol new file mode 100644 index 000000000..5db5ec121 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_no_send.sol @@ -0,0 +1,8 @@ +contract C { + saddress payable a; + function f() external returns (bool) { + return a.send(1); + } +} +// ---- +// TypeError 3125: (95-101): Member "send" not found or not visible after argument-dependent lookup in saddress payable. Cast to address first: "address(a).send". diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_no_staticcall.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_no_staticcall.sol new file mode 100644 index 000000000..cd19c3fdd --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_no_staticcall.sol @@ -0,0 +1,8 @@ +contract C { + saddress a; + function f() external view returns (bool, bytes memory) { + return a.staticcall(""); + } +} +// ---- +// TypeError 3125: (106-118): Member "staticcall" not found or not visible after argument-dependent lookup in saddress. Cast to address first: "address(a).staticcall". diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_no_transfer.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_no_transfer.sol new file mode 100644 index 000000000..c0264a6be --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_no_transfer.sol @@ -0,0 +1,8 @@ +contract C { + saddress payable a; + function f() external { + a.transfer(1); + } +} +// ---- +// TypeError 3125: (73-83): Member "transfer" not found or not visible after argument-dependent lookup in saddress payable. Cast to address first: "address(a).transfer". From 7d5cccc070e2695e66015a0ce7b1f5e2d94cf07f Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 15:38:38 -0500 Subject: [PATCH 40/73] fix(types): restrict saddress members to code and codehash only Override nativeMembers() in ShieldedAddressType to only expose code and codehash members. Other address members (balance, call, delegatecall, staticcall, send, transfer) are intentionally not available on shielded addresses - users should cast to address first if needed. Add helpful error message in TypeChecker suggesting to cast to address when accessing disallowed members on shielded addresses. Also fix TypeChecker to check type category instead of dynamic_cast when handling send/transfer errors, since ShieldedAddressType inherits from AddressType but should not match the payable address assertion. --- libsolidity/analysis/TypeChecker.cpp | 19 ++++++++++++++++++- libsolidity/ast/Types.cpp | 10 ++++++++++ libsolidity/ast/Types.h | 4 ++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 16632c0db..0d04a7434 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3399,11 +3399,28 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) return { 3125_error, errorMsg }; } } - else if (auto const* addressType = dynamic_cast(exprType)) + else if (exprType->category() == Type::Category::ShieldedAddress) + { + // Shielded addresses only expose code and codehash. + // For other address members, suggest casting to address. + for (MemberList::Member const& addressMember: TypeProvider::payableAddress()->nativeMembers(nullptr)) + if (addressMember.name == memberName) + { + auto const* var = dynamic_cast(&_memberAccess.expression()); + std::string varName = var ? var->name() : "..."; + errorMsg += " Cast to address first: \"address(" + varName + ")." + memberName + "\"."; + return { 3125_error, errorMsg }; + } + } + else if (exprType->category() == Type::Category::Address) { // Trigger error when using send or transfer with a non-payable fallback function. + // Note: ShieldedAddressType inherits from AddressType but has its own category, + // so this only matches regular address types. if (memberName == "send" || memberName == "transfer") { + auto const* addressType = dynamic_cast(exprType); + solAssert(addressType, ""); solAssert( addressType->stateMutability() != StateMutability::Payable, "Expected address not-payable as members were not found" diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index fe28de802..9d82bf836 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -569,6 +569,16 @@ MemberList::MemberMap AddressType::nativeMembers(ASTNode const*) const return members; } +MemberList::MemberMap ShieldedAddressType::nativeMembers(ASTNode const*) const +{ + // Only code and codehash are allowed on shielded addresses. + // For balance, call, delegatecall, staticcall, send, transfer: cast to address first. + return MemberList::MemberMap{ + {"code", TypeProvider::array(DataLocation::Memory)}, + {"codehash", TypeProvider::fixedBytes(32)} + }; +} + std::string ShieldedAddressType::richIdentifier() const { if (stateMutability() == StateMutability::Payable) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index bdc3a3f70..77bdc91e8 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -506,6 +506,10 @@ class ShieldedAddressType: public AddressType std::string toString(bool _withoutDataLocation) const override; std::string canonicalName() const override; + /// Only code and codehash are allowed on shielded addresses. + /// For other members, cast to address first. + MemberList::MemberMap nativeMembers(ASTNode const*) const override; + StateMutability stateMutability(void) const { return AddressType::stateMutability(); } }; From 473efad365db20bdf115b93e2004901e34ad3fa9 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 16:17:52 -0500 Subject: [PATCH 41/73] test(codegen): add tests for UDT wrapping shielded primitives Add tests to verify that user-defined types wrapping shielded primitives (sbool, suint, sint, saddress, sbytes) are correctly recognized as shielded: - Syntax tests for assembly validation (sload/sstore rejected on UDT-wrapped shielded types) - Semantic test for storage read/write operations - Update existing ASTJSON tests to use internal visibility (shielded types can't be public) --- .../shielded_userDefinedValueType.json | 11 ++-- .../ASTJSON/shielded_userDefinedValueType.sol | 2 +- .../shielded_user_defined_operator.json | 31 ++++++----- .../shielded_user_defined_operator.sol | 2 +- .../userDefinedValueType/shielded_storage.sol | 53 +++++++++++++++++++ .../shielded_sload_on_udt_suint.sol | 12 +++++ .../shielded_sstore_on_udt_sbool.sol | 12 +++++ .../shielded_sstore_on_udt_suint.sol | 12 +++++ 8 files changed, 111 insertions(+), 24 deletions(-) create mode 100644 test/libsolidity/semanticTests/userDefinedValueType/shielded_storage.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_on_udt_suint.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_udt_sbool.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_udt_suint.sol diff --git a/test/libsolidity/ASTJSON/shielded_userDefinedValueType.json b/test/libsolidity/ASTJSON/shielded_userDefinedValueType.json index a3880b220..f6f73789d 100644 --- a/test/libsolidity/ASTJSON/shielded_userDefinedValueType.json +++ b/test/libsolidity/ASTJSON/shielded_userDefinedValueType.json @@ -235,14 +235,13 @@ }, { "constant": false, - "functionSelector": "97682884", "id": 26, "mutability": "mutable", "name": "m", - "nameLocation": "207:1:1", + "nameLocation": "209:1:1", "nodeType": "VariableDeclaration", "scope": 27, - "src": "171:37:1", + "src": "171:39:1", "stateVariable": true, "storageLocation": "default", "typeDescriptions": { @@ -302,14 +301,14 @@ } } }, - "visibility": "public" + "visibility": "internal" } ], "scope": 28, - "src": "97:114:1", + "src": "97:116:1", "usedErrors": [], "usedEvents": [] } ], - "src": "0:212:1" + "src": "0:214:1" } diff --git a/test/libsolidity/ASTJSON/shielded_userDefinedValueType.sol b/test/libsolidity/ASTJSON/shielded_userDefinedValueType.sol index 0c85e6a0f..3af2c68ec 100644 --- a/test/libsolidity/ASTJSON/shielded_userDefinedValueType.sol +++ b/test/libsolidity/ASTJSON/shielded_userDefinedValueType.sol @@ -7,7 +7,7 @@ function f() { contract C { type MyAddress is address; type MyUInt is suint; - mapping(MyAddress => MyUInt) public m; + mapping(MyAddress => MyUInt) internal m; } // ---- diff --git a/test/libsolidity/ASTJSON/shielded_user_defined_operator.json b/test/libsolidity/ASTJSON/shielded_user_defined_operator.json index 7aaeac0c1..8f114a0c4 100644 --- a/test/libsolidity/ASTJSON/shielded_user_defined_operator.json +++ b/test/libsolidity/ASTJSON/shielded_user_defined_operator.json @@ -361,7 +361,7 @@ "body": { "id": 47, "nodeType": "Block", - "src": "209:30:1", + "src": "211:30:1", "statements": [ { "expression": { @@ -385,14 +385,14 @@ "nodeType": "UnaryOperation", "operator": "-", "prefix": true, - "src": "226:2:1", + "src": "228:2:1", "subExpression": { "id": 42, "name": "a", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 33, - "src": "227:1:1", + "src": "229:1:1", "typeDescriptions": { "typeIdentifier": "t_userDefinedValueType$_I8_$2", "typeString": "I8" @@ -411,13 +411,13 @@ "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 36, - "src": "231:1:1", + "src": "233:1:1", "typeDescriptions": { "typeIdentifier": "t_userDefinedValueType$_I8_$2", "typeString": "I8" } }, - "src": "226:6:1", + "src": "228:6:1", "typeDescriptions": { "typeIdentifier": "t_userDefinedValueType$_I8_$2", "typeString": "I8" @@ -426,11 +426,10 @@ "functionReturnParameters": 41, "id": 46, "nodeType": "Return", - "src": "219:13:1" + "src": "221:13:1" } ] }, - "functionSelector": "c7cc07ab", "id": 48, "implemented": true, "kind": "function", @@ -531,7 +530,7 @@ "nameLocation": "-1:-1:-1", "nodeType": "VariableDeclaration", "scope": 48, - "src": "205:2:1", + "src": "207:2:1", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { @@ -545,14 +544,14 @@ "id": 38, "name": "I8", "nameLocations": [ - "205:2:1" + "207:2:1" ], "nodeType": "IdentifierPath", "referencedDeclaration": 2, - "src": "205:2:1" + "src": "207:2:1" }, "referencedDeclaration": 2, - "src": "205:2:1", + "src": "207:2:1", "typeDescriptions": { "typeIdentifier": "t_userDefinedValueType$_I8_$2", "typeString": "I8" @@ -561,20 +560,20 @@ "visibility": "internal" } ], - "src": "204:4:1" + "src": "206:4:1" }, "scope": 49, - "src": "161:78:1", + "src": "161:80:1", "stateMutability": "pure", "virtual": false, - "visibility": "public" + "visibility": "internal" } ], "scope": 50, - "src": "144:97:1", + "src": "144:99:1", "usedErrors": [], "usedEvents": [] } ], - "src": "0:242:1" + "src": "0:244:1" } diff --git a/test/libsolidity/ASTJSON/shielded_user_defined_operator.sol b/test/libsolidity/ASTJSON/shielded_user_defined_operator.sol index 22d234ae7..6e2fbe948 100644 --- a/test/libsolidity/ASTJSON/shielded_user_defined_operator.sol +++ b/test/libsolidity/ASTJSON/shielded_user_defined_operator.sol @@ -3,7 +3,7 @@ using {sub as -, unsub as -} for I8 global; function sub(I8, I8) pure returns (I8) {} function unsub(I8) pure returns (I8) {} contract C { - function f(I8 a, I8 b) public pure returns (I8) { + function f(I8 a, I8 b) internal pure returns (I8) { return -a - b; } } diff --git a/test/libsolidity/semanticTests/userDefinedValueType/shielded_storage.sol b/test/libsolidity/semanticTests/userDefinedValueType/shielded_storage.sol new file mode 100644 index 000000000..8e7598f87 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/shielded_storage.sol @@ -0,0 +1,53 @@ +// Test user-defined types wrapping shielded primitives correctly use cload/cstore +type MyShieldedUint is suint256; +type MyShieldedBool is sbool; +type MyShieldedAddress is saddress; +type MyShieldedBytes32 is sbytes32; + +contract C { + MyShieldedUint internal storedUint; + MyShieldedBool internal storedBool; + MyShieldedAddress internal storedAddress; + MyShieldedBytes32 internal storedBytes; + + function setUint(uint256 x) external { + storedUint = MyShieldedUint.wrap(suint256(x)); + } + + function getUint() external view returns (uint256) { + return uint256(MyShieldedUint.unwrap(storedUint)); + } + + function setBool(bool x) external { + storedBool = MyShieldedBool.wrap(sbool(x)); + } + + function getBool() external view returns (bool) { + return bool(MyShieldedBool.unwrap(storedBool)); + } + + function setAddress(address x) external { + storedAddress = MyShieldedAddress.wrap(saddress(x)); + } + + function getAddress() external view returns (address) { + return address(MyShieldedAddress.unwrap(storedAddress)); + } + + function setBytes(bytes32 x) external { + storedBytes = MyShieldedBytes32.wrap(sbytes32(x)); + } + + function getBytes() external view returns (bytes32) { + return bytes32(MyShieldedBytes32.unwrap(storedBytes)); + } +} +// ---- +// setUint(uint256): 42 -> +// getUint() -> 42 +// setBool(bool): true -> +// getBool() -> true +// setAddress(address): 0x1234567890123456789012345678901234567890 -> +// getAddress() -> 0x1234567890123456789012345678901234567890 +// setBytes(bytes32): 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef -> +// getBytes() -> 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_on_udt_suint.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_on_udt_suint.sol new file mode 100644 index 000000000..6cf1b8f15 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sload_on_udt_suint.sol @@ -0,0 +1,12 @@ +type MyShieldedUint is suint256; + +contract C { + MyShieldedUint x; + function f() external view returns (uint256 result) { + assembly { + result := sload(x.slot) + } + } +} +// ---- +// TypeError 5765: (168-181): Cannot use sload() on shielded storage variable. Use cload() instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_udt_sbool.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_udt_sbool.sol new file mode 100644 index 000000000..68c5e1a90 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_udt_sbool.sol @@ -0,0 +1,12 @@ +type MyShieldedBool is sbool; + +contract C { + MyShieldedBool x; + function f() external { + assembly { + sstore(x.slot, 1) + } + } +} +// ---- +// TypeError 5765: (125-142): Cannot use sstore() on shielded storage variable. Use cstore() instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_udt_suint.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_udt_suint.sol new file mode 100644 index 000000000..8d80723f9 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_sstore_on_udt_suint.sol @@ -0,0 +1,12 @@ +type MyShieldedUint is suint256; + +contract C { + MyShieldedUint x; + function f() external { + assembly { + sstore(x.slot, 42) + } + } +} +// ---- +// TypeError 5765: (128-146): Cannot use sstore() on shielded storage variable. Use cstore() instead. From 5fbd4d4bb332114898804e1cd7511fb9c41db0f9 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 16:17:57 -0500 Subject: [PATCH 42/73] fix(codegen): support shielded primitives in user-defined types UserDefinedValueType now correctly delegates isShielded() and containsShieldedType() to the underlying type. This ensures that UDTs wrapping shielded primitives (e.g., `type MyUint is suint256`) use cload/cstore instead of sload/sstore for storage operations. --- libsolidity/ast/Types.cpp | 16 ++++++++++++++++ libsolidity/ast/Types.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 9d82bf836..a932fba6a 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2923,6 +2923,22 @@ Type const& UserDefinedValueType::underlyingType() const return *type; } +bool UserDefinedValueType::isShielded() const +{ + Type const* type = m_definition.underlyingType()->annotation().type; + if (!type) + return false; + return type->isShielded(); +} + +bool UserDefinedValueType::containsShieldedType() const +{ + Type const* type = m_definition.underlyingType()->annotation().type; + if (!type) + return false; + return type->containsShieldedType(); +} + Declaration const* UserDefinedValueType::typeDefinition() const { return &m_definition; diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 77bdc91e8..b4a7830d4 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1287,6 +1287,9 @@ class UserDefinedValueType: public Type u256 storageSize() const override { return underlyingType().storageSize(); } unsigned storageBytes() const override { return underlyingType().storageBytes(); } + bool isShielded() const override; + bool containsShieldedType() const override; + bool isValueType() const override { return true; } bool nameable() const override { From f85a66a9545e8591b2538856c8bbee2eb976185a Mon Sep 17 00:00:00 2001 From: cdrappi Date: Thu, 22 Jan 2026 16:25:29 -0500 Subject: [PATCH 43/73] test: add regression tests for shielded array storage operations Add tests to verify that copying shielded arrays uses cstore/cload instead of sstore/sload. These tests currently fail because the implementation hardcodes sstore/sload regardless of type shielding. Tests cover: - Memory-to-storage copy - Storage-to-storage copy - Calldata-to-storage copy - Multiple items per slot (sbool[]) Each test verifies: - Initial array length is 0 before copy - Array length after copy is correct - Array elements are correctly stored and retrieved Addresses: Veridise audit finding VER-771 --- ...hielded_array_copy_calldata_to_storage.sol | 22 ++++++++++ .../shielded_array_copy_memory_to_storage.sol | 23 +++++++++++ .../shielded_array_copy_sbool_storage.sol | 31 ++++++++++++++ ...shielded_array_copy_storage_to_storage.sol | 41 +++++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 test/libsolidity/semanticTests/array/copying/shielded_array_copy_calldata_to_storage.sol create mode 100644 test/libsolidity/semanticTests/array/copying/shielded_array_copy_memory_to_storage.sol create mode 100644 test/libsolidity/semanticTests/array/copying/shielded_array_copy_sbool_storage.sol create mode 100644 test/libsolidity/semanticTests/array/copying/shielded_array_copy_storage_to_storage.sol diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_calldata_to_storage.sol new file mode 100644 index 000000000..f04e4cb15 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_calldata_to_storage.sol @@ -0,0 +1,22 @@ +// Tests that copying shielded arrays from calldata to storage uses cstore +contract C { + suint[] b; + + function f(suint[] calldata c) public { + b = c; + } + + function getLength() public view returns (uint256) { + return b.length; + } + + function get(uint256 i) public view returns (uint256) { + return uint256(b[i]); + } +} +// ---- +// getLength() -> 0x00 +// f(suint256[]): 0x20, 0x05, 0x64, 0x65, 0x66, 0x67, 0x68 -> +// getLength() -> 0x05 +// get(uint256): 0 -> 0x64 +// get(uint256): 4 -> 0x68 diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_memory_to_storage.sol new file mode 100644 index 000000000..89e8e1d79 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_memory_to_storage.sol @@ -0,0 +1,23 @@ +// Tests that copying shielded arrays from memory to storage uses cstore/cload +contract C { + suint[] b; + + function f(suint[] memory m) public { + b = m; + } + + function getLength() public view returns (uint256) { + return b.length; + } + + function get(uint256 i) public view returns (uint256) { + return uint256(b[i]); + } +} +// ---- +// getLength() -> 0x00 +// f(suint256[]): 0x20, 0x03, 0x01, 0x02, 0x03 -> +// getLength() -> 0x03 +// get(uint256): 0 -> 0x01 +// get(uint256): 1 -> 0x02 +// get(uint256): 2 -> 0x03 diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_sbool_storage.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_sbool_storage.sol new file mode 100644 index 000000000..89d3d440a --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_sbool_storage.sol @@ -0,0 +1,31 @@ +// Tests that copying shielded bool arrays (multiple items per slot) uses cstore/cload +contract C { + sbool[] a; + sbool[] b; + + function setA(sbool[] memory m) public { + a = m; + } + + function copyAtoB() public { + b = a; + } + + function getLengthB() public view returns (uint256) { + return b.length; + } + + function getB(uint256 i) public view returns (bool) { + return bool(b[i]); + } +} +// ---- +// getLengthB() -> 0x00 +// setA(sbool[]): 0x20, 0x05, 0x01, 0x00, 0x01, 0x01, 0x00 -> +// copyAtoB() -> +// getLengthB() -> 0x05 +// getB(uint256): 0 -> true +// getB(uint256): 1 -> false +// getB(uint256): 2 -> true +// getB(uint256): 3 -> true +// getB(uint256): 4 -> false diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_storage_to_storage.sol new file mode 100644 index 000000000..961ff8d8a --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_storage_to_storage.sol @@ -0,0 +1,41 @@ +// Tests that copying shielded arrays from storage to storage uses cstore/cload +contract C { + suint[] a; + suint[] b; + + function setA(suint[] memory m) public { + a = m; + } + + function copyAtoB() public { + b = a; + } + + function getLengthA() public view returns (uint256) { + return a.length; + } + + function getLengthB() public view returns (uint256) { + return b.length; + } + + function getA(uint256 i) public view returns (uint256) { + return uint256(a[i]); + } + + function getB(uint256 i) public view returns (uint256) { + return uint256(b[i]); + } +} +// ---- +// getLengthA() -> 0x00 +// getLengthB() -> 0x00 +// setA(suint256[]): 0x20, 0x04, 0x0a, 0x0b, 0x0c, 0x0d -> +// getLengthA() -> 0x04 +// getA(uint256): 1 -> 0x0b +// copyAtoB() -> +// getLengthB() -> 0x04 +// getB(uint256): 0 -> 0x0a +// getB(uint256): 1 -> 0x0b +// getB(uint256): 2 -> 0x0c +// getB(uint256): 3 -> 0x0d From 5354fc5081174ae8942a4f8609a952251be051ff Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 16:56:04 -0500 Subject: [PATCH 44/73] test(codegen): add regression tests for shielded storage bugs Add tests that expose bugs in VER-771 (Yul array utils use SSTORE). The original fix addressed copyValueArrayToStorageFunction but missed several other locations: 1. bytes_push_with_value.sol - Tests bytes.push(value) including the long array case (>31 elements) which exposes the bug where sload was used instead of sstore in storageArrayPushFunction. 2. shielded_array_push_with_value.sol - Tests that pushing values to shielded arrays uses cstore for the actual storage write. 3. shielded_compact_array_abi_encode.sol - Tests that ABI encoding compact shielded storage arrays (like sbool[]) uses cload. 4. shielded_struct_abi_encode.sol - Tests that ABI encoding structs with shielded members uses cload for the shielded fields. These tests will fail without the corresponding fix. --- .../array/bytes_push_with_value.sol | 48 +++++++++++++++++++ .../array/shielded_array_push_with_value.sol | 33 +++++++++++++ .../shielded_compact_array_abi_encode.sol | 31 ++++++++++++ .../structs/shielded_struct_abi_encode.sol | 30 ++++++++++++ 4 files changed, 142 insertions(+) create mode 100644 test/libsolidity/semanticTests/array/bytes_push_with_value.sol create mode 100644 test/libsolidity/semanticTests/array/shielded_array_push_with_value.sol create mode 100644 test/libsolidity/semanticTests/array/shielded_compact_array_abi_encode.sol create mode 100644 test/libsolidity/semanticTests/structs/shielded_struct_abi_encode.sol diff --git a/test/libsolidity/semanticTests/array/bytes_push_with_value.sol b/test/libsolidity/semanticTests/array/bytes_push_with_value.sol new file mode 100644 index 000000000..bef48b8c0 --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_push_with_value.sol @@ -0,0 +1,48 @@ +// Tests bytes.push(value) functionality +// This test exposes a bug where sload was incorrectly used instead of sstore +// in the storageArrayPushFunction when pushing to a bytes array with >31 elements +contract C { + bytes data; + + function pushByte(bytes1 b) public { + data.push(b); + } + + function getLength() public view returns (uint256) { + return data.length; + } + + function getByte(uint256 i) public view returns (bytes1) { + return data[i]; + } + + // Push enough bytes to transition from short to long array (>31 bytes) + function fillTo32() public { + for (uint i = 0; i < 32; i++) { + data.push(bytes1(uint8(i + 1))); + } + } + + function pushMultiple() public { + data.push(0x01); + data.push(0x02); + data.push(0x03); + } +} +// ---- +// getLength() -> 0 +// pushByte(bytes1): left(0x42) -> +// getLength() -> 1 +// getByte(uint256): 0 -> left(0x42) +// pushMultiple() -> +// getLength() -> 4 +// getByte(uint256): 1 -> left(0x01) +// getByte(uint256): 2 -> left(0x02) +// getByte(uint256): 3 -> left(0x03) +// fillTo32() -> +// getLength() -> 36 +// getByte(uint256): 4 -> left(0x01) +// getByte(uint256): 35 -> left(0x20) +// pushByte(bytes1): left(0xAB) -> +// getLength() -> 37 +// getByte(uint256): 36 -> left(0xAB) diff --git a/test/libsolidity/semanticTests/array/shielded_array_push_with_value.sol b/test/libsolidity/semanticTests/array/shielded_array_push_with_value.sol new file mode 100644 index 000000000..20b8ef718 --- /dev/null +++ b/test/libsolidity/semanticTests/array/shielded_array_push_with_value.sol @@ -0,0 +1,33 @@ +// Tests shielded array push with value functionality +// This test verifies that storageArrayPushFunction uses cstore for shielded types +contract C { + suint[] data; + + function pushValue(uint256 v) public { + data.push(suint256(v)); + } + + function getLength() public view returns (uint256) { + return data.length; + } + + function getValue(uint256 i) public view returns (uint256) { + return uint256(data[i]); + } + + function pushMultiple() public { + data.push(suint256(100)); + data.push(suint256(200)); + data.push(suint256(300)); + } +} +// ---- +// getLength() -> 0 +// pushValue(uint256): 42 -> +// getLength() -> 1 +// getValue(uint256): 0 -> 42 +// pushMultiple() -> +// getLength() -> 4 +// getValue(uint256): 1 -> 100 +// getValue(uint256): 2 -> 200 +// getValue(uint256): 3 -> 300 diff --git a/test/libsolidity/semanticTests/array/shielded_compact_array_abi_encode.sol b/test/libsolidity/semanticTests/array/shielded_compact_array_abi_encode.sol new file mode 100644 index 000000000..3225bc541 --- /dev/null +++ b/test/libsolidity/semanticTests/array/shielded_compact_array_abi_encode.sol @@ -0,0 +1,31 @@ +// Tests ABI encoding of compact shielded storage arrays +// This test exposes a bug where sload was used instead of cload +// in abiEncodingFunctionCompactStorageArray for shielded types +// sbool packs multiple items per slot, triggering the compact array code path +contract C { + sbool[] flags; + + function setup() public { + flags.push(sbool(true)); + flags.push(sbool(false)); + flags.push(sbool(true)); + flags.push(sbool(false)); + } + + function getFlags() public view returns (bool[] memory) { + bool[] memory result = new bool[](flags.length); + for (uint i = 0; i < flags.length; i++) { + result[i] = bool(flags[i]); + } + return result; + } + + function getLength() public view returns (uint256) { + return flags.length; + } +} +// ---- +// getLength() -> 0 +// setup() -> +// getLength() -> 4 +// getFlags() -> 0x20, 4, true, false, true, false diff --git a/test/libsolidity/semanticTests/structs/shielded_struct_abi_encode.sol b/test/libsolidity/semanticTests/structs/shielded_struct_abi_encode.sol new file mode 100644 index 000000000..9db58e837 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/shielded_struct_abi_encode.sol @@ -0,0 +1,30 @@ +// Tests ABI encoding of structs with shielded members from storage +// This test exposes a bug where sload was used instead of cload +// in abiEncodingFunctionStruct for shielded struct members +contract C { + struct Data { + suint256 secretValue; + uint256 publicValue; + suint256 anotherSecret; + } + + Data stored; + + function setup(uint256 secret1, uint256 pub, uint256 secret2) public { + stored.secretValue = suint256(secret1); + stored.publicValue = pub; + stored.anotherSecret = suint256(secret2); + } + + function getStored() public view returns (uint256, uint256, uint256) { + return ( + uint256(stored.secretValue), + stored.publicValue, + uint256(stored.anotherSecret) + ); + } +} +// ---- +// getStored() -> 0, 0, 0 +// setup(uint256,uint256,uint256): 42, 100, 77 -> +// getStored() -> 42, 100, 77 From 85f62ae9d5ebd1b89217cdd9a06c31a76dfeed70 Mon Sep 17 00:00:00 2001 From: cdrappi Date: Thu, 22 Jan 2026 16:25:29 -0500 Subject: [PATCH 45/73] fix: use cstore/cload for shielded array storage operations This fixes a critical issue where storage array copy operations hardcoded sstore/sload regardless of whether the base type was shielded. For shielded value types like suint[] and sbool[], this would violate confidentiality guarantees and could cause reverts when accessing confidential storage slots. Changes: - Modified copyValueArrayToStorageFunction to select opcodes based on base type's isShielded() status - Fixed hardcoded sload in updateSrcPtr template to use parameterized loadOpcode for both single and multi-item-per-slot cases The generated IR now correctly emits cstore/cload for shielded arrays and sstore/sload for non-shielded arrays. Addresses: Veridise audit finding VER-771 --- libsolidity/codegen/YulUtilFunctions.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index bd3c2392b..d71db8ed4 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -2241,8 +2241,8 @@ std::string YulUtilFunctions::copyValueArrayToStorageFunction(ArrayType const& _ unsigned itemsPerSlot = 32 / _toType.storageStride(); templ("itemsPerSlot", std::to_string(itemsPerSlot)); templ("multipleItemsPerSlotDst", itemsPerSlot > 1); - templ("storeOpcode", "sstore"); - templ("loadOpcode", "sload"); + templ("storeOpcode", _toType.baseType()->isShielded() ? "cstore" : "sstore"); + templ("loadOpcode", _fromType.baseType()->isShielded() ? "cload" : "sload"); bool sameTypeFromStorage = fromStorage && (*_fromType.baseType() == *_toType.baseType()); if (auto functionType = dynamic_cast(_fromType.baseType())) { @@ -2272,16 +2272,17 @@ std::string YulUtilFunctions::copyValueArrayToStorageFunction(ArrayType const& _ if eq(srcItemIndexInSlot, ) { // here we are done with this slot, we need to read next one srcPtr := add(srcPtr, 1) - srcSlotValue := sload(srcPtr) + srcSlotValue := (srcPtr) srcItemIndexInSlot := 0 } srcPtr := add(srcPtr, 1) - srcSlotValue := sload(srcPtr) + srcSlotValue := (srcPtr) )") ("srcReadMultiPerSlot", !sameTypeFromStorage && _fromType.storageStride() <= 16) ("srcItemsPerSlot", std::to_string(32 / _fromType.storageStride())) + ("loadOpcode", _fromType.baseType()->isShielded() ? "cload" : "sload") .render() ); else From 7e6593e9b727e6f37242cf81f5c4f3315464dbc3 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Tue, 3 Feb 2026 16:56:22 -0500 Subject: [PATCH 46/73] fix(codegen): complete shielded storage opcode fixes for VER-771 This completes the fix for VER-771 (Yul array utils use SSTORE) which was partially addressed in a previous commit. The original fix only covered copyValueArrayToStorageFunction, but several other locations were missed. Changes: 1. YulUtilFunctions.cpp - storageArrayPushFunction(): - Fixed sload(array, add(data, 2)) -> sstore() in byte array path (sload only takes 1 argument, this was broken code) - Fixed storeValue -> to use templated function name 2. ABIFunctions.cpp - abiEncodingFunctionCompactStorageArray(): - Changed hardcoded sload to template variable - Added loadOpcode selection based on _from.baseType()->isShielded() 3. ABIFunctions.cpp - abiEncodingFunctionStruct(): - Changed hardcoded sload to select cload/sload based on memberTypeFrom->isShielded() These changes ensure that: - Shielded storage arrays use cload/cstore for confidential data - Non-shielded arrays continue to use sload/sstore - The bytes.push(value) functionality works correctly for long arrays --- libsolidity/codegen/ABIFunctions.cpp | 8 +++++--- libsolidity/codegen/YulUtilFunctions.cpp | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 474848760..9fc5597e1 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -750,7 +750,7 @@ std::string ABIFunctions::abiEncodingFunctionCompactStorageArray( for { } lt(add(itemCounter, sub(, 1)), length) { itemCounter := add(itemCounter, ) } { - let data := sload(srcPtr) + let data := (srcPtr) <#items> ((data), pos) pos := add(pos, ) @@ -760,7 +760,7 @@ std::string ABIFunctions::abiEncodingFunctionCompactStorageArray( } // Handle the last (not necessarily full) slot specially if { - let data := sload(srcPtr) + let data := (srcPtr) <#items> if { ((data), pos) @@ -781,6 +781,7 @@ std::string ABIFunctions::abiEncodingFunctionCompactStorageArray( templ("lengthFun", m_utils.arrayLengthFunction(_from)); templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options)); templ("dataArea", m_utils.arrayDataAreaFunction(_from)); + templ("loadOpcode", _from.baseType()->isShielded() ? "cload" : "sload"); // We skip the loop for arrays that fit a single slot. if (_from.isDynamicallySized() || _from.length() >= itemsPerSlot) templ("useLoop", "1"); @@ -894,7 +895,8 @@ std::string ABIFunctions::abiEncodingFunctionStruct( { if (storageSlotOffset != previousSlotOffset) { - members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"; + std::string loadOpcode = memberTypeFrom->isShielded() ? "cload" : "sload"; + members.back()["preprocess"] = "slotValue := " + loadOpcode + "(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"; previousSlotOffset = storageSlotOffset; } members.back()["retrieveValue"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset) + "(slotValue)"; diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index d71db8ed4..07df3c709 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1744,9 +1744,9 @@ std::string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, T } } default { - sload(array, add(data, 2)) + sstore(array, add(data, 2)) let slot, offset := (array, oldLen) - storeValue(slot, offset ) + (slot, offset ) } let oldLen := (array) From d4ca185696c65e20fe656830896d1b811309a4bc Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Thu, 5 Feb 2026 11:49:15 -0500 Subject: [PATCH 47/73] test(types): add regression tests for UDVT shielded mapping keys Add syntax tests verifying that user-defined value types wrapping shielded types (sbool, suint256, saddress) are rejected as mapping keys. Also includes a positive test confirming non-shielded UDVTs remain allowed. These tests validate the fix in commit 5fbd4d4bb which added isShielded() delegation to UserDefinedValueType. --- .../shieldedTypes/udvt_nonshielded_mapping_key.sol | 6 ++++++ .../syntaxTests/shieldedTypes/udvt_shielded_mapping_key.sol | 6 ++++++ .../shieldedTypes/udvt_shielded_mapping_key_saddress.sol | 6 ++++++ .../shieldedTypes/udvt_shielded_mapping_key_suint.sol | 6 ++++++ 4 files changed, 24 insertions(+) create mode 100644 test/libsolidity/syntaxTests/shieldedTypes/udvt_nonshielded_mapping_key.sol create mode 100644 test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key.sol create mode 100644 test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key_saddress.sol create mode 100644 test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key_suint.sol diff --git a/test/libsolidity/syntaxTests/shieldedTypes/udvt_nonshielded_mapping_key.sol b/test/libsolidity/syntaxTests/shieldedTypes/udvt_nonshielded_mapping_key.sol new file mode 100644 index 000000000..f05b73cd6 --- /dev/null +++ b/test/libsolidity/syntaxTests/shieldedTypes/udvt_nonshielded_mapping_key.sol @@ -0,0 +1,6 @@ +// Non-shielded user defined value types should still be allowed as mapping keys +type MyUint is uint256; +contract C { + mapping(MyUint => uint256) m; +} +// ---- diff --git a/test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key.sol b/test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key.sol new file mode 100644 index 000000000..187380ffc --- /dev/null +++ b/test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key.sol @@ -0,0 +1,6 @@ +contract C { + type SB is sbool; + mapping(SB => uint256) m; +} +// ---- +// TypeError 7804: (47-49): Shielded types are not allowed as mapping keys. diff --git a/test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key_saddress.sol b/test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key_saddress.sol new file mode 100644 index 000000000..43a0a0722 --- /dev/null +++ b/test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key_saddress.sol @@ -0,0 +1,6 @@ +contract C { + type SA is saddress; + mapping(SA => uint256) m; +} +// ---- +// TypeError 7804: (50-52): Shielded types are not allowed as mapping keys. diff --git a/test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key_suint.sol b/test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key_suint.sol new file mode 100644 index 000000000..fd9927b4b --- /dev/null +++ b/test/libsolidity/syntaxTests/shieldedTypes/udvt_shielded_mapping_key_suint.sol @@ -0,0 +1,6 @@ +contract C { + type SU is suint256; + mapping(SU => uint256) m; +} +// ---- +// TypeError 7804: (50-52): Shielded types are not allowed as mapping keys. From 3a3fbf98ec8a8659fea12afcb668ca038661569d Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Thu, 5 Feb 2026 12:01:18 -0500 Subject: [PATCH 48/73] test(codegen): add regression tests for address/saddress to enum conversion Add syntax tests to document that address and saddress types cannot be explicitly converted to enum types. These tests complement the codegen guard that prevents such conversions at runtime. --- .../133_address_to_enum_conversion_not_allowed.sol | 8 ++++++++ ...ielded_133_saddress_to_enum_conversion_not_allowed.sol | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/133_address_to_enum_conversion_not_allowed.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_133_saddress_to_enum_conversion_not_allowed.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/133_address_to_enum_conversion_not_allowed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/133_address_to_enum_conversion_not_allowed.sol new file mode 100644 index 000000000..316ec8d7c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/133_address_to_enum_conversion_not_allowed.sol @@ -0,0 +1,8 @@ +contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function f(address a) public pure returns (ActionChoices) { + return ActionChoices(a); + } +} +// ---- +// TypeError 9640: (155-171): Explicit type conversion not allowed from "address" to "enum test.ActionChoices". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_133_saddress_to_enum_conversion_not_allowed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_133_saddress_to_enum_conversion_not_allowed.sol new file mode 100644 index 000000000..b7ee33165 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_133_saddress_to_enum_conversion_not_allowed.sol @@ -0,0 +1,8 @@ +contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function f(saddress a) public pure returns (ActionChoices) { + return ActionChoices(a); + } +} +// ---- +// TypeError 9640: (156-172): Explicit type conversion not allowed from "saddress" to "enum test.ActionChoices". From 6847fe78a0b3a891db6c24f97dda95d2cf13dad0 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Thu, 5 Feb 2026 12:01:24 -0500 Subject: [PATCH 49/73] fix(codegen): correct enum conversion guard for address/saddress types The guard in CompilerUtils::convertType used || instead of &&, making the assertion always true (a tautology). The condition `(A != X || A != Y)` is always true because a value cannot equal both X and Y simultaneously. Changed to && so the assertion correctly fails when the type is either Address or ShieldedAddress. --- libsolidity/codegen/CompilerUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 6fe394384..6f790363e 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -933,7 +933,7 @@ void CompilerUtils::convertType( } else if (targetTypeCategory == Type::Category::Enum) { - solAssert((stackTypeCategory != Type::Category::Address || stackTypeCategory != Type::Category::ShieldedAddress), "Invalid conversion to EnumType requested."); + solAssert((stackTypeCategory != Type::Category::Address && stackTypeCategory != Type::Category::ShieldedAddress), "Invalid conversion to EnumType requested."); solAssert(_typeOnStack.mobileType()); // just clean convertType(_typeOnStack, *_typeOnStack.mobileType(), true); From 959c4a3e482ab6dd0deb4d84b5afa1c015bc1918 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Thu, 5 Feb 2026 12:23:33 -0500 Subject: [PATCH 50/73] test(codegen): add regression tests for bytes/string push bug Add additional semantic tests for the storageArrayPushFunction bug where sload was incorrectly used instead of sstore and storeValue was missing template brackets for long byte/string arrays. Tests cover: - bytes_push_long_array: pushing to arrays already in long encoding - bytes_push_boundary_transition: short-to-long array transition - bytes_push_length_update: verifies length updates correctly - string_push_via_bytes: tests string path via bytes(str).push() --- .../array/bytes_push_boundary_transition.sol | 42 +++++++++++++++++++ .../array/bytes_push_length_update.sol | 37 ++++++++++++++++ .../array/bytes_push_long_array.sol | 41 ++++++++++++++++++ .../array/string_push_via_bytes.sol | 40 ++++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 test/libsolidity/semanticTests/array/bytes_push_boundary_transition.sol create mode 100644 test/libsolidity/semanticTests/array/bytes_push_length_update.sol create mode 100644 test/libsolidity/semanticTests/array/bytes_push_long_array.sol create mode 100644 test/libsolidity/semanticTests/array/string_push_via_bytes.sol diff --git a/test/libsolidity/semanticTests/array/bytes_push_boundary_transition.sol b/test/libsolidity/semanticTests/array/bytes_push_boundary_transition.sol new file mode 100644 index 000000000..e645c1dd7 --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_push_boundary_transition.sol @@ -0,0 +1,42 @@ +// Tests bytes.push() around the short/long array boundary (31 bytes) +// Regression test for storageArrayPushFunction bug +contract C { + bytes data; + + function pushByte(bytes1 b) public { + data.push(b); + } + + function getLength() public view returns (uint256) { + return data.length; + } + + function getByte(uint256 i) public view returns (bytes1) { + return data[i]; + } + + // Fill to exactly 30 bytes (still short array) + function fillTo30() public { + for (uint i = 0; i < 30; i++) { + data.push(bytes1(uint8(0x10 + i))); + } + } +} +// ---- +// getLength() -> 0 +// fillTo30() -> +// getLength() -> 30 +// getByte(uint256): 0 -> left(0x10) +// getByte(uint256): 29 -> left(0x2d) +// pushByte(bytes1): left(0xE1) -> +// getLength() -> 31 +// getByte(uint256): 30 -> left(0xE1) +// pushByte(bytes1): left(0xE2) -> +// getLength() -> 32 +// getByte(uint256): 31 -> left(0xE2) +// pushByte(bytes1): left(0xE3) -> +// getLength() -> 33 +// getByte(uint256): 32 -> left(0xE3) +// pushByte(bytes1): left(0xE4) -> +// getLength() -> 34 +// getByte(uint256): 33 -> left(0xE4) diff --git a/test/libsolidity/semanticTests/array/bytes_push_length_update.sol b/test/libsolidity/semanticTests/array/bytes_push_length_update.sol new file mode 100644 index 000000000..e52e02eef --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_push_length_update.sol @@ -0,0 +1,37 @@ +// Tests that bytes.push(value) correctly updates length for long arrays +// Regression test: sload was used instead of sstore, failing to update length +contract C { + bytes data; + + function setup() public { + // Create a 64-byte array (well into long array territory) + for (uint i = 0; i < 64; i++) { + data.push(bytes1(uint8(i))); + } + } + + function pushAndReturnLength(bytes1 b) public returns (uint256) { + data.push(b); + return data.length; + } + + function getLength() public view returns (uint256) { + return data.length; + } + + function getByte(uint256 i) public view returns (bytes1) { + return data[i]; + } +} +// ---- +// setup() -> +// getLength() -> 64 +// pushAndReturnLength(bytes1): left(0xFF) -> 65 +// getLength() -> 65 +// getByte(uint256): 64 -> left(0xFF) +// pushAndReturnLength(bytes1): left(0xFE) -> 66 +// getLength() -> 66 +// getByte(uint256): 65 -> left(0xFE) +// pushAndReturnLength(bytes1): left(0xFD) -> 67 +// getLength() -> 67 +// getByte(uint256): 66 -> left(0xFD) diff --git a/test/libsolidity/semanticTests/array/bytes_push_long_array.sol b/test/libsolidity/semanticTests/array/bytes_push_long_array.sol new file mode 100644 index 000000000..608430255 --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_push_long_array.sol @@ -0,0 +1,41 @@ +// Tests bytes.push(value) for long arrays (>31 bytes) +// Regression test for bug where sload was used instead of sstore +// and storeValue was missing template brackets in storageArrayPushFunction +contract C { + bytes data; + + // Fill array to exactly 32 bytes (transition to long array encoding) + function setup() public { + for (uint i = 0; i < 32; i++) { + data.push(bytes1(uint8(i))); + } + } + + // Push to long array - this exercises the buggy code path + function pushToLongArray(bytes1 b) public { + data.push(b); + } + + function getLength() public view returns (uint256) { + return data.length; + } + + function getByte(uint256 i) public view returns (bytes1) { + return data[i]; + } +} +// ---- +// getLength() -> 0 +// setup() -> +// getLength() -> 32 +// getByte(uint256): 0 -> left(0x00) +// getByte(uint256): 31 -> left(0x1f) +// pushToLongArray(bytes1): left(0xAA) -> +// getLength() -> 33 +// getByte(uint256): 32 -> left(0xAA) +// pushToLongArray(bytes1): left(0xBB) -> +// getLength() -> 34 +// getByte(uint256): 33 -> left(0xBB) +// pushToLongArray(bytes1): left(0xCC) -> +// getLength() -> 35 +// getByte(uint256): 34 -> left(0xCC) diff --git a/test/libsolidity/semanticTests/array/string_push_via_bytes.sol b/test/libsolidity/semanticTests/array/string_push_via_bytes.sol new file mode 100644 index 000000000..42a5ebb19 --- /dev/null +++ b/test/libsolidity/semanticTests/array/string_push_via_bytes.sol @@ -0,0 +1,40 @@ +// Tests string storage push via bytes cast for long strings +// Regression test for storageArrayPushFunction bug (affects isByteArrayOrString) +contract C { + string data; + + function setup() public { + // Create a 40-character string (long string encoding) + data = "1234567890123456789012345678901234567890"; + } + + function pushChar(bytes1 b) public { + bytes(data).push(b); + } + + function getLength() public view returns (uint256) { + return bytes(data).length; + } + + function getChar(uint256 i) public view returns (bytes1) { + return bytes(data)[i]; + } + + function getString() public view returns (string memory) { + return data; + } +} +// ---- +// setup() -> +// getLength() -> 40 +// getChar(uint256): 0 -> left(0x31) +// getChar(uint256): 39 -> left(0x30) +// pushChar(bytes1): left(0x41) -> +// getLength() -> 41 +// getChar(uint256): 40 -> left(0x41) +// pushChar(bytes1): left(0x42) -> +// getLength() -> 42 +// getChar(uint256): 41 -> left(0x42) +// pushChar(bytes1): left(0x43) -> +// getLength() -> 43 +// getChar(uint256): 42 -> left(0x43) From 491cec472de7958be36cfeb29096eee5b26f5176 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Thu, 5 Feb 2026 14:58:14 -0500 Subject: [PATCH 51/73] test(codegen): add syntax test for byte array helpers with non-shielded types Verify that bytes/string storage operations (push, pop, resize, assign) compile correctly. These operations use byte array helpers that should only emit sload/sstore since there is no shielded dynamic bytes type (sbytesN is a fixed-size value type, not a dynamic byte array). --- .../types/sbytes_no_dynamic_byte_array.sol | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol diff --git a/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol b/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol new file mode 100644 index 000000000..0f96ae9f0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol @@ -0,0 +1,33 @@ +// Byte array helpers (resize, push, pop, transitLongToShort) only apply to +// bytes/string. There is no shielded dynamic bytes type. sbytesN is a +// fixed-size value type that does not route through byte array helpers. +contract C { + bytes public b; + string public s; + sbytes32 sb; + + // bytes/string use byte array storage helpers (sload/sstore only) + function testBytesPushPop() public { + b.push(0x01); + b.push(0x02); + b.pop(); + } + + function testBytesResize() public { + b = new bytes(64); + b = new bytes(16); + b = new bytes(0); + } + + function testStringAssign() public { + s = "short"; + s = "this is a long string that exceeds thirty two bytes in length!!"; + s = ""; + } + + // sbytesN is a fixed-size value type, not a dynamic byte array + function testSbytesN() public { + sb = sbytes32(0x0102030405060708091011121314151617181920212223242526272829303132); + } +} +// ---- From 545ed832b71051858dc7dbd2de55a54b2b9730be Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Thu, 5 Feb 2026 15:32:49 -0500 Subject: [PATCH 52/73] fix(codegen): check base type instead of array category for shielded opcode selection Four byte array helper functions (resizeDynamicByteArrayFunction, increaseByteArraySizeFunction, byteArrayTransitLongToShortFunction, storageByteArrayPopFunction) chose between sload/cload and sstore/cstore based on _type.category() == ShieldedInteger. Since _type is an ArrayType, its category is always Array, making the shielded branch unreachable. Fix by checking _type.baseType()->isShielded() instead, which correctly inspects the element type. This has no behavioral change today (bytes/string have a non-shielded base type) but prepares these helpers for a future shielded dynamic bytes type. --- libsolidity/codegen/YulUtilFunctions.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 07df3c709..87b2ec473 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1457,7 +1457,7 @@ std::string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _t } )") ("extractLength", extractByteArrayLengthFunction()) - ("loadOpcode", _type.category() == Type::Category::ShieldedInteger ? "cload" : "sload") + ("loadOpcode", _type.baseType()->isShielded() ? "cload" : "sload") ("decreaseSize", decreaseByteArraySizeFunction(_type)) ("increaseSize", increaseByteArraySizeFunction(_type)) .render(); @@ -1562,7 +1562,7 @@ std::string YulUtilFunctions::increaseByteArraySizeFunction(ArrayType const& _ty ("maxArrayLength", (u256(1) << 64).str()) ("dataPosition", arrayDataAreaFunction(_type)) ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) - ("storeOpcode", _type.category() == Type::Category::ShieldedInteger ? "cstore" : "sstore") + ("storeOpcode", _type.baseType()->isShielded() ? "cstore" : "sstore") .render(); }); } @@ -1583,8 +1583,8 @@ std::string YulUtilFunctions::byteArrayTransitLongToShortFunction(ArrayType cons ("functionName", functionName) ("dataPosition", arrayDataAreaFunction(_type)) ("extractUsedApplyLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) - ("storeOpcode", _type.category() == Type::Category::ShieldedInteger ? "cstore" : "sstore") - ("loadOpcode", _type.category() == Type::Category::ShieldedInteger ? "cload" : "sload") + ("storeOpcode", _type.baseType()->isShielded() ? "cstore" : "sstore") + ("loadOpcode", _type.baseType()->isShielded() ? "cload" : "sload") .render(); }); } @@ -1694,7 +1694,7 @@ std::string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type ("transitLongToShort", byteArrayTransitLongToShortFunction(_type)) ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) ("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction()) - ("loadOpcode", _type.category() == Type::Category::ShieldedInteger ? "cload" : "sload") + ("loadOpcode", _type.baseType()->isShielded() ? "cload" : "sload") ("setToZero", storageSetToZeroFunction(*_type.baseType(), VariableDeclaration::Location::Unspecified)) .render(); }); From 5bde633e375fe806252255b63fd95572cb8a8c22 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Wed, 4 Feb 2026 19:34:28 -0500 Subject: [PATCH 53/73] test(analysis): add syntax tests for shielded types in ABI encoding Add tests verifying the compiler rejects shielded types in: - abi.encode() with suint256, saddress, sbool, structs, and arrays - abi.encodePacked() with shielded types - abi.encodeWithSelector() and abi.encodeWithSignature() - abi.decode() with shielded type targets Update existing tests to expect the new error and cast shielded NONCE to uint96 in AESEncryptAndDecryptWithRng semantic test. These tests expose a vulnerability where shielded values could be serialized to plaintext bytes via ABI encoding, leaking confidential data and allowing recovery via abi.decode. --- .../seismic_precompiles/AESEncryptAndDecryptWithRng.sol | 4 ++-- .../abiEncoder/abi_decode_shielded_address.sol | 7 +++++++ .../syntaxTests/abiEncoder/abi_decode_shielded_bool.sol | 7 +++++++ .../abiEncoder/abi_decode_shielded_struct.sol | 8 ++++++++ .../syntaxTests/abiEncoder/abi_decode_shielded_types.sol | 7 +++++++ .../abiEncoder/abi_encode_packed_shielded.sol | 9 +++++++++ .../abiEncoder/abi_encode_shielded_address.sol | 8 ++++++++ .../syntaxTests/abiEncoder/abi_encode_shielded_array.sol | 8 ++++++++ .../abiEncoder/abi_encode_shielded_struct.sol | 8 ++++++++ .../syntaxTests/abiEncoder/abi_encode_shielded_types.sol | 9 +++++++++ .../abiEncoder/abi_encode_with_selector_shielded.sol | 9 +++++++++ .../abiEncoder/abi_encode_with_signature_shielded.sol | 9 +++++++++ .../types/address/shielded_address_abi_decode.sol | 1 + 13 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_address.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_bool.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_struct.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_types.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_packed_shielded.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_address.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_array.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_struct.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_types.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_selector_shielded.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_signature_shielded.sol diff --git a/test/libsolidity/semanticTests/functionCall/seismic_precompiles/AESEncryptAndDecryptWithRng.sol b/test/libsolidity/semanticTests/functionCall/seismic_precompiles/AESEncryptAndDecryptWithRng.sol index a2ce0112e..a269dd5b7 100644 --- a/test/libsolidity/semanticTests/functionCall/seismic_precompiles/AESEncryptAndDecryptWithRng.sol +++ b/test/libsolidity/semanticTests/functionCall/seismic_precompiles/AESEncryptAndDecryptWithRng.sol @@ -20,7 +20,7 @@ contract AES { function encryptAndStore() public { address AESEncryptAddr = address(0x66); - bytes memory input = abi.encodePacked(AES_KEY, NONCE, PLAINTEXT); + bytes memory input = abi.encodePacked(AES_KEY, uint96(NONCE), PLAINTEXT); (bool success, bytes memory output) = AESEncryptAddr.staticcall(input); require(success, "Precompile encryption call failed"); @@ -34,7 +34,7 @@ contract AES { address AESDecryptAddr = address(0x67); - bytes memory input = abi.encodePacked(AES_KEY, NONCE, encryptedData); + bytes memory input = abi.encodePacked(AES_KEY, uint96(NONCE), encryptedData); (bool success, bytes memory output) = AESDecryptAddr.staticcall(input); require(success, "Precompile decryption call failed"); diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_address.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_address.sol new file mode 100644 index 000000000..0fc04a053 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_address.sol @@ -0,0 +1,7 @@ +contract C { + function f(bytes memory data) public pure { + abi.decode(data, (saddress)); + } +} +// ---- +// TypeError 4851: (87-95): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_bool.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_bool.sol new file mode 100644 index 000000000..3cf0dfda4 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_bool.sol @@ -0,0 +1,7 @@ +contract C { + function f(bytes memory data) public pure { + abi.decode(data, (sbool)); + } +} +// ---- +// TypeError 4851: (87-92): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_struct.sol new file mode 100644 index 000000000..e76d6035a --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_struct.sol @@ -0,0 +1,8 @@ +contract C { + struct S { suint256 a; suint256 b; } + function f(bytes memory data) public pure { + abi.decode(data, (S)); + } +} +// ---- +// TypeError 4851: (128-129): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_types.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_types.sol new file mode 100644 index 000000000..2790d7d9a --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_decode_shielded_types.sol @@ -0,0 +1,7 @@ +contract C { + function f(bytes memory data) public pure { + abi.decode(data, (suint256)); + } +} +// ---- +// TypeError 4851: (87-95): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_packed_shielded.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_packed_shielded.sol new file mode 100644 index 000000000..343184bcd --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_packed_shielded.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + sbool b = sbool(true); + abi.encodePacked(b); + } +} +// ---- +// Warning 9661: (52-73): Bool Literals converted to shielded bools will leak during contract deployment. +// TypeError 3648: (100-101): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_address.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_address.sol new file mode 100644 index 000000000..466f7367a --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_address.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + saddress a = saddress(0); + abi.encode(a); + } +} +// ---- +// TypeError 3648: (97-98): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_array.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_array.sol new file mode 100644 index 000000000..ee294cdf1 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_array.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + suint256[] memory arr = new suint256[](1); + abi.encode(arr); + } +} +// ---- +// TypeError 3648: (114-117): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_struct.sol new file mode 100644 index 000000000..b60a3f4c2 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_struct.sol @@ -0,0 +1,8 @@ +contract C { + struct S { suint256 a; suint256 b; } + function f(S memory s) internal pure { + abi.encode(s); + } +} +// ---- +// TypeError 3648: (116-117): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_types.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_types.sol new file mode 100644 index 000000000..a2d5b210f --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_types.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + suint256 a = suint256(1); + abi.encode(a); + } +} +// ---- +// Warning 9660: (52-76): Literals converted to shielded integers will leak during contract deployment. +// TypeError 3648: (97-98): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_selector_shielded.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_selector_shielded.sol new file mode 100644 index 000000000..e943ed001 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_selector_shielded.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + suint256 a = suint256(1); + abi.encodeWithSelector(bytes4(0x12345678), a); + } +} +// ---- +// Warning 9660: (52-76): Literals converted to shielded integers will leak during contract deployment. +// TypeError 3648: (129-130): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_signature_shielded.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_signature_shielded.sol new file mode 100644 index 000000000..f960c34a6 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_signature_shielded.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + suint256 a = suint256(1); + abi.encodeWithSignature("foo(uint256)", a); + } +} +// ---- +// Warning 9660: (52-76): Literals converted to shielded integers will leak during contract deployment. +// TypeError 3648: (126-127): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_abi_decode.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_abi_decode.sol index 684e8c097..a2f49cde1 100644 --- a/test/libsolidity/syntaxTests/types/address/shielded_address_abi_decode.sol +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_abi_decode.sol @@ -5,3 +5,4 @@ contract C { } } // ---- +// TypeError 4851: (130-138): Shielded types cannot be ABI encoded. From 3c9014018f4e2fe76e23733cbf77ce33e968e255 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Wed, 4 Feb 2026 19:34:36 -0500 Subject: [PATCH 54/73] fix(analysis): reject shielded types in ABI encoding and decoding Add type checks in TypeChecker to prevent shielded values from being serialized through ABI encoding functions, which would leak confidential data as plaintext bytes. Changes: - typeCheckABIDecodeAndRetrieveReturnType(): reject shielded types in abi.decode() target types (error 4851) - typeCheckABIEncodeFunctions(): reject shielded types in abi.encode(), abi.encodePacked(), abi.encodeWithSelector(), and abi.encodeWithSignature() (error 3648) - typeCheckABIEncodeCallFunction(): reject shielded types in abi.encodeCall() arguments (error 3648) Uses containsShieldedType() to catch both direct shielded types and composite types (structs, arrays) containing shielded fields. Checks are guarded with else-if to only trigger when the type would otherwise be valid for encoding, avoiding interference with existing error paths. --- libsolidity/analysis/TypeChecker.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0d04a7434..32c2985e3 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -168,6 +168,12 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c typeArgument->location(), "Decoding type " + actualType->humanReadableName() + " not supported." ); + else if (actualType->containsShieldedType()) + m_errorReporter.typeError( + 4851_error, + typeArgument->location(), + "Shielded types cannot be ABI encoded." + ); if (auto referenceType = dynamic_cast(actualType)) { @@ -2424,6 +2430,12 @@ void TypeChecker::typeCheckABIEncodeFunctions( arguments[i]->location(), "This type cannot be encoded." ); + else if (argType->containsShieldedType()) + m_errorReporter.typeError( + 3648_error, + arguments[i]->location(), + "Shielded types cannot be ABI encoded." + ); } } @@ -2577,6 +2589,12 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa "\"" + (result.message().empty() ? "." : ": " + result.message()) ); + else if (argType.containsShieldedType()) + m_errorReporter.typeError( + 3648_error, + callArguments[i]->location(), + "Shielded types cannot be ABI encoded." + ); } } From a671413390dac72cffbdbfdaf713388ec155d475 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 6 Feb 2026 11:20:32 -0500 Subject: [PATCH 55/73] fix(codegen): incorrect store/load instruction when copying (unimplemented) shielded bytes (#176) Replace remaining hardcoded sstore/sload with templated storeOpcode/ loadOpcode in byte array storage helpers, using baseType()->isShielded() to select between cstore/cload and sstore/sload. This sets up dead code to be correct for when sbytes (dynamic shielded bytes) is implemented. Functions fixed: - copyByteArrayToStorageFunction: 5 sstore + 1 sload + read template - decreaseByteArraySizeFunction: 2 sstore - increaseByteArraySizeFunction: 3 sstore - storageByteArrayPopFunction: 2 sstore - storageArrayPushFunction: 5 sstore/sload in byte array path --- libsolidity/codegen/YulUtilFunctions.cpp | 51 ++++++++++++++---------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 87b2ec473..0fdf25dea 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1505,7 +1505,7 @@ std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _ty (deleteStart, add(arrayDataStart, (oldLen))) - sstore(array, or(mul(2, newLen), 1)) + (array, or(mul(2, newLen), 1)) } default { switch gt(oldLen, 31) @@ -1516,7 +1516,7 @@ std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _ty (array, newLen) } default { - sstore(array, (data, newLen)) + (array, (data, newLen)) } } })") @@ -1527,6 +1527,7 @@ std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _ty ("transitLongToShort", byteArrayTransitLongToShortFunction(_type)) ("div32Ceil", divide32CeilFunction()) ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) + ("storeOpcode", _type.baseType()->isShielded() ? "cstore" : "sstore") .render(); }); } @@ -1542,7 +1543,7 @@ std::string YulUtilFunctions::increaseByteArraySizeFunction(ArrayType const& _ty switch lt(oldLen, 32) case 0 { // in this case array stays unpacked, so we just set new length - sstore(array, add(mul(2, newLen), 1)) + (array, add(mul(2, newLen), 1)) } default { switch lt(newLen, 32) @@ -1550,11 +1551,11 @@ std::string YulUtilFunctions::increaseByteArraySizeFunction(ArrayType const& _ty // we need to copy elements to data area as we changed array from packed to unpacked data := and(not(0xff), data) ((array), data) - sstore(array, add(mul(2, newLen), 1)) + (array, add(mul(2, newLen), 1)) } default { // here array stays packed, we just need to increase length - sstore(array, (data, newLen)) + (array, (data, newLen)) } } )") @@ -1679,12 +1680,12 @@ std::string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type let newLen := sub(oldLen, 1) switch lt(oldLen, 32) case 1 { - sstore(array, (data, newLen)) + (array, (data, newLen)) } default { let slot, offset := (array, newLen) (slot, offset) - sstore(array, sub(data, 2)) + (array, sub(data, 2)) } } })") @@ -1694,6 +1695,7 @@ std::string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type ("transitLongToShort", byteArrayTransitLongToShortFunction(_type)) ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) ("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction()) + ("storeOpcode", _type.baseType()->isShielded() ? "cstore" : "sstore") ("loadOpcode", _type.baseType()->isShielded() ? "cload" : "sload") ("setToZero", storageSetToZeroFunction(*_type.baseType(), VariableDeclaration::Location::Unspecified)) .render(); @@ -1717,7 +1719,7 @@ std::string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, T return Whiskers(R"( function (array ) { - let data := sload(array) + let data := (array) let oldLen := (data) if iszero(lt(oldLen, )) { () } @@ -1730,9 +1732,9 @@ std::string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, T // We need to copy data let dataArea := (array) data := and(data, not(0xff)) - sstore(dataArea, or(and(0xff, value), data)) + (dataArea, or(and(0xff, value), data)) // New length is 32, encoded as (32 * 2 + 1) - sstore(array, 65) + (array, 65) } default { data := add(data, 2) @@ -1740,11 +1742,11 @@ std::string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, T let valueShifted := (shiftBits, and(0xff, value)) let mask := (shiftBits, 0xff) data := or(and(data, not(mask)), valueShifted) - sstore(array, data) + (array, data) } } default { - sstore(array, add(data, 2)) + (array, add(data, 2)) let slot, offset := (array, oldLen) (slot, offset ) } @@ -1757,13 +1759,17 @@ std::string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, T })") ("functionName", functionName) - // Array length is always stored in public storage, even for shielded arrays - ("storeOpcode", "sstore") + // TODO: Decide whether shielded byte array (sbytes) length should be public or private. + // For short arrays (<=31 bytes), data and length share a slot, so cstore forces length private. + // For long arrays, length is in its own slot and could be public—but that changes visibility + // based on array size. Currently all other shielded array lengths are public (sstore). + // For non-byte arrays, length is always stored in public storage, even for shielded arrays. + ("storeOpcode", _type.isByteArrayOrString() ? (_type.baseType()->isShielded() ? "cstore" : "sstore") : "sstore") ("values", _fromType->sizeOnStack() == 0 ? "" : ", " + suffixedVariableNameList("value", 0, _fromType->sizeOnStack())) ("panic", panicFunction(PanicCode::ResourceError)) ("extractByteArrayLength", _type.isByteArrayOrString() ? extractByteArrayLengthFunction() : "") ("dataAreaFunction", arrayDataAreaFunction(_type)) - ("loadOpcode", "sload") + ("loadOpcode", _type.isByteArrayOrString() ? (_type.baseType()->isShielded() ? "cload" : "sload") : "sload") ("isByteArrayOrString", _type.isByteArrayOrString()) ("indexAccess", storageArrayIndexAccessFunction(_type)) ("storeValue", updateStorageValueFunction(*_fromType, *_type.baseType(), VariableDeclaration::Location::Unspecified)) @@ -2068,7 +2074,7 @@ std::string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _f // Make sure array length is sane if gt(newLen, 0xffffffffffffffff) { () } - let oldLen := (sload(slot)) + let oldLen := ((slot)) // potentially truncate data (slot, oldLen, newLen) @@ -2085,22 +2091,22 @@ std::string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _f let dstPtr := (slot) let i := 0 for { } lt(i, loopEnd) { i := add(i, 0x20) } { - sstore(dstPtr, (add(src, srcOffset))) + (dstPtr, (add(src, srcOffset))) dstPtr := add(dstPtr, 1) srcOffset := add(srcOffset, ) } if lt(loopEnd, newLen) { let lastValue := (add(src, srcOffset)) - sstore(dstPtr, (lastValue, and(newLen, 0x1f))) + (dstPtr, (lastValue, and(newLen, 0x1f))) } - sstore(slot, add(mul(newLen, 2), 1)) + (slot, add(mul(newLen, 2), 1)) } default { let value := 0 if newLen { value := (add(src, srcOffset)) } - sstore(slot, (value, newLen)) + (slot, (value, newLen)) } } )"); @@ -2118,7 +2124,10 @@ std::string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _f templ("srcDataLocation", arrayDataAreaFunction(_fromType)); templ("cleanUpEndArray", cleanUpDynamicByteArrayEndSlotsFunction(_toType)); templ("srcIncrement", std::to_string(fromStorage ? 1 : 0x20)); - templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload"); + bool toIsShielded = _toType.baseType()->isShielded(); + templ("storeOpcode", toIsShielded ? "cstore" : "sstore"); + templ("loadOpcode", toIsShielded ? "cload" : "sload"); + templ("read", fromStorage ? (_fromType.baseType()->isShielded() ? "cload" : "sload") : fromCalldata ? "calldataload" : "mload"); templ("maskBytes", maskBytesFunctionDynamic()); templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction()); From 1199286bb15fb003fbb8fe12862d3605deb11b69 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 6 Feb 2026 10:28:34 -0500 Subject: [PATCH 56/73] test(codegen): add regression tests for struct delete with mixed shielded fields Covers the case where delete on a struct containing both shielded and non-shielded fields incorrectly clears packed non-shielded slots with cstore instead of sstore. --- .../shielded_struct_delete_mixed_fields.sol | 17 +++++++++++++++ ...ed_struct_delete_mixed_multiple_packed.sol | 21 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_fields.sol create mode 100644 test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_multiple_packed.sol diff --git a/test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_fields.sol b/test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_fields.sol new file mode 100644 index 000000000..35093553d --- /dev/null +++ b/test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_fields.sol @@ -0,0 +1,17 @@ +contract C { + struct S { + uint8 a; + suint256 b; + } + + S s; + + function f() public returns (uint8) { + s.a = 42; + s.b = suint256(7); + delete s; + return s.a; + } +} +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_multiple_packed.sol b/test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_multiple_packed.sol new file mode 100644 index 000000000..406ac684e --- /dev/null +++ b/test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_multiple_packed.sol @@ -0,0 +1,21 @@ +contract C { + struct S { + uint8 a; + bool b; + suint256 c; + uint128 d; + } + + S s; + + function f() public returns (uint8, bool, uint128) { + s.a = 42; + s.b = true; + s.c = suint256(99); + s.d = 12345; + delete s; + return (s.a, s.b, s.d); + } +} +// ---- +// f() -> 0, false, 0 From f6e0052d541315b0fea0fcfd6e4971c20f52d86a Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 6 Feb 2026 10:28:44 -0500 Subject: [PATCH 57/73] fix(codegen): use sstore for sub-32-byte fields in struct delete clear clearStorageStructFunction used containsShieldedType() as a struct-level flag to choose cstore vs sstore for all slot clears. For mixed structs this caused non-shielded packed fields to be cleared with cstore while their getters use sload, reverting under privacy rules. Since all shielded types have storageBytes() == 32, the storageBytes < 32 branch can only ever process non-shielded fields. Hardcode sstore in that path and remove the unused struct-level flag. --- libsolidity/codegen/YulUtilFunctions.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 0fdf25dea..de70d09b8 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1903,8 +1903,6 @@ std::string YulUtilFunctions::clearStorageStructFunction(StructType const& _type std::string functionName = "clear_struct_storage_" + _type.identifier(); - bool isShielded = _type.containsShieldedType(); - return m_functionCollector.createFunction(functionName, [&] { MemberList::MemberMap structMembers = _type.nativeMembers(nullptr); std::vector> memberSetValues; @@ -1916,13 +1914,21 @@ std::string YulUtilFunctions::clearStorageStructFunction(StructType const& _type continue; if (member.type->storageBytes() < 32) { + // All shielded types have storageBytes() == 32, so only non-shielded + // types can reach this branch. Use sstore accordingly. If a future + // shielded type with storageBytes() < 32 is introduced, this assert + // will catch it so the opcode selection can be updated. + solAssert( + !member.type->isShielded(), + "Shielded type with storageBytes() < 32 requires cstore, not sstore" + ); auto const& slotDiff = _type.storageOffsetsOfMember(member.name).first; if (!slotsCleared.count(slotDiff)) { memberSetValues.emplace_back().emplace("clearMember", Whiskers(R"( (add(slot, ), 0) )") - ("storeOpcode", isShielded ? "cstore" : "sstore") + ("storeOpcode", "sstore") ("memberSlotDiff", slotDiff.str()) .render() ); From b9d98678e9757ff16efaa347724fea90e0588b70 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 6 Feb 2026 11:30:41 -0500 Subject: [PATCH 58/73] test(analysis): add regression tests for mismatched privacy opcodes Add 4 tests covering the auditor's finding that mixing cstore/sload in inline assembly compiles but reverts at runtime: - shielded_cstore_caller_then_sload: auditor's exact POC (TypeError 5768) - shielded_cstore_then_sload_var_slot: variable-based slot matching (TypeError 5768) - shielded_cstore_then_sload_dynamic_slot_ok: documents known limitation where dynamic slots cannot be caught at compile time - cstore_then_sload_dynamic_slot_should_fail: proves dynamic slot case correctly reverts at runtime The core fix already exists in validateShieldedStorageOps() (ad883e1). These tests confirm coverage of the auditor's specific scenarios and the acknowledged limitation around dynamic slot expressions. --- ...store_then_sload_dynamic_slot_should_fail.sol | 16 ++++++++++++++++ .../shielded_cstore_caller_then_sload.sol | 14 ++++++++++++++ ...hielded_cstore_then_sload_dynamic_slot_ok.sol | 12 ++++++++++++ .../shielded_cstore_then_sload_var_slot.sol | 12 ++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_dynamic_slot_should_fail.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_caller_then_sload.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload_dynamic_slot_ok.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload_var_slot.sol diff --git a/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_dynamic_slot_should_fail.sol b/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_dynamic_slot_should_fail.sol new file mode 100644 index 000000000..78b0e7c02 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/cstore_then_sload_dynamic_slot_should_fail.sol @@ -0,0 +1,16 @@ +contract C { + function test(uint slot) external returns (uint) { + assembly { + cstore(0, 42) + } + // Separate block to avoid compile-time detection. + // When slot == 0, sload should revert because the slot is now private. + assembly { + let x := sload(slot) + mstore(0, x) + return(0, 32) + } + } +} +// ---- +// test(uint256): 0 -> FAILURE diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_caller_then_sload.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_caller_then_sload.sol new file mode 100644 index 000000000..12e63554f --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_caller_then_sload.sol @@ -0,0 +1,14 @@ +// Audit regression: cstore with caller() value followed by sload on same literal slot +contract C { + event e(uint); + function test() external { + uint c; + assembly { + cstore(0, caller()) + c := sload(0) + } + emit e(c); + } +} +// ---- +// TypeError 5768: (234-242): Cannot use sload() on a slot that was previously written with cstore(). cstore() makes the slot private, and sload() cannot access private storage. Use cload() instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload_dynamic_slot_ok.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload_dynamic_slot_ok.sol new file mode 100644 index 000000000..b466e3c90 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload_dynamic_slot_ok.sol @@ -0,0 +1,12 @@ +// Known limitation: dynamic slot prevents compile-time detection. +// cstore(0, 0) then sload(slot) with dynamic slot cannot be matched statically. +// Will revert at runtime if slot == 0. +contract C { + function test(uint slot) external returns (uint c) { + assembly { + cstore(0, 0) + c := sload(slot) + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload_var_slot.sol b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload_var_slot.sol new file mode 100644 index 000000000..32334ef87 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shielded_cstore_then_sload_var_slot.sol @@ -0,0 +1,12 @@ +// Variable-based slot matching: same Yul variable for cstore and sload slots +contract C { + function test() external returns (uint c) { + assembly { + let s := 0 + cstore(s, 42) + c := sload(s) + } + } +} +// ---- +// TypeError 5768: (224-232): Cannot use sload() on a slot that was previously written with cstore(). cstore() makes the slot private, and sload() cannot access private storage. Use cload() instead. From 70b588d9fbcecfc9220da5c7345dc2c94ad38de3 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 6 Feb 2026 13:19:15 -0500 Subject: [PATCH 59/73] test(ir): disable tests that require --via-ir --- test/libsolidity/StandardCompiler.cpp | 3 +++ test/solc/CommandLineInterface.cpp | 4 ++++ test/solc/CommandLineParser.cpp | 11 ++++++++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 84e40fabf..4ce52dc94 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -1921,6 +1921,8 @@ BOOST_AUTO_TEST_CASE(ethdebug_excluded_from_wildcards) BOOST_REQUIRE(result.dump().find("ethdebug") == std::string::npos); } +// NOTE: ethdebug tests skipped (require via-ir pipeline which is disabled) +#if 0 BOOST_AUTO_TEST_CASE(ethdebug_debug_info_ethdebug) { static std::vector>>> tests{ @@ -2257,6 +2259,7 @@ BOOST_DATA_TEST_CASE(ethdebug_output_instructions_smoketest, boost::unit_test::d BOOST_REQUIRE(!instruction["operation"].contains("arguments")); } } +#endif BOOST_AUTO_TEST_SUITE_END() diff --git a/test/solc/CommandLineInterface.cpp b/test/solc/CommandLineInterface.cpp index adfd24b02..00c720622 100644 --- a/test/solc/CommandLineInterface.cpp +++ b/test/solc/CommandLineInterface.cpp @@ -1527,6 +1527,9 @@ BOOST_AUTO_TEST_CASE(cli_ethdebug_incompatible_input_modes) } } +// NOTE: cli_ethdebug_debug_info_ethdebug and cli_ethdebug_ethdebug_output tests +// skipped (require --via-ir which is disabled) +#if 0 BOOST_AUTO_TEST_CASE(cli_ethdebug_debug_info_ethdebug) { TemporaryDirectory tempDir(TEST_CASE_NAME); @@ -1696,6 +1699,7 @@ BOOST_AUTO_TEST_CASE(cli_ethdebug_ethdebug_output) BOOST_REQUIRE(result.success); } } +#endif BOOST_AUTO_TEST_SUITE_END() diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 85eeaebe3..377e3e178 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -118,8 +118,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "--output-dir=/tmp/out", "--overwrite", "--evm-version=spuriousDragon", - "--via-ir", - "--experimental-via-ir", + // NOTE: --via-ir and --experimental-via-ir removed (via-ir pipeline disabled) "--revert-strings=strip", "--debug-info=location", "--pretty-json", @@ -180,7 +179,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) expectedOptions.output.dir = "/tmp/out"; expectedOptions.output.overwriteFiles = true; expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); - expectedOptions.output.viaIR = true; + expectedOptions.output.viaIR = false; expectedOptions.output.revertStrings = RevertStrings::Strip; expectedOptions.output.debugInfoSelection = DebugInfoSelection::fromString("location"); expectedOptions.formatting.json = JsonFormat{JsonFormat::Pretty, 7}; @@ -260,12 +259,15 @@ BOOST_AUTO_TEST_CASE(no_import_callback) } } +// NOTE: via_ir_options test skipped (via-ir pipeline disabled) +#if 0 BOOST_AUTO_TEST_CASE(via_ir_options) { BOOST_TEST(!parseCommandLine({"solc", "contract.sol"}).output.viaIR); for (std::string viaIrOption: {"--via-ir", "--experimental-via-ir"}) BOOST_TEST(parseCommandLine({"solc", viaIrOption, "contract.sol"}).output.viaIR); } +#endif BOOST_AUTO_TEST_CASE(assembly_mode_options) { @@ -628,6 +630,8 @@ BOOST_AUTO_TEST_CASE(invalid_optimizer_sequence_without_optimize) } } +// NOTE: ethdebug test skipped (requires --via-ir which is disabled) +#if 0 BOOST_AUTO_TEST_CASE(ethdebug) { CommandLineOptions commandLineOptions = parseCommandLine({"solc", "contract.sol", "--debug-info", "ethdebug", "--ethdebug", "--via-ir"}); @@ -664,6 +668,7 @@ BOOST_AUTO_TEST_CASE(ethdebug) BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection.has_value(), true); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection->ethdebug, true); } +#endif BOOST_AUTO_TEST_SUITE_END() From fe61bfe2560468db414bc3bca8918c5e3c9e8f9b Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 6 Feb 2026 13:19:19 -0500 Subject: [PATCH 60/73] ci(ir): use --skip-via-ir for semantic tests until via-ir is re-enabled --- .github/workflows/test.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c3e5c418c..531078965 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -86,9 +86,12 @@ jobs: - name: Run soltest run: ./scripts/soltest.sh + # TODO: Remove the hardcoded commit checkout once we merge the --skip-via-ir flag into the seismic branch of seismic-revm - name: Clone seismic-revm repo run: | git clone https://github.com/SeismicSystems/seismic-revm.git /tmp/seismic-revm + cd /tmp/seismic-revm + git checkout b0d6010e26e365c41cb3eb05eba774e2510e861d echo "SEISMIC_REVM_PATH=/tmp/seismic-revm" >> $GITHUB_ENV echo "seismic-revm cloned to: /tmp/seismic-revm" - uses: Swatinem/rust-cache@v2 @@ -108,7 +111,7 @@ jobs: [ -x "$SSOLC_EXEC" ] || { echo "Error: solc not found at $SSOLC_EXEC"; exit 1; } [ -x "$SEISMIC_REVME_EXEC" ] || { echo "Error: revme executable not found at $SEISMIC_REVME_EXEC"; exit 1; } SEMANTIC_TESTS_DIR="$GITHUB_WORKSPACE/test/libsolidity/semanticTests" - RUST_LOG=info RUST_BACKTRACE=1 $SEISMIC_REVME_EXEC semantics --keep-going -s "$SSOLC_EXEC" -t "$SEMANTIC_TESTS_DIR" 2>&1 + RUST_LOG=info RUST_BACKTRACE=1 $SEISMIC_REVME_EXEC semantics --keep-going -s "$SSOLC_EXEC" -t "$SEMANTIC_TESTS_DIR" --skip-via-ir 2>&1 - name: Run semantic tests (with optimizer) run: | set -euo pipefail @@ -116,7 +119,9 @@ jobs: [ -x "$SSOLC_EXEC" ] || { echo "Error: solc not found at $SSOLC_EXEC"; exit 1; } [ -x "$SEISMIC_REVME_EXEC" ] || { echo "Error: revme executable not found at $SEISMIC_REVME_EXEC"; exit 1; } SEMANTIC_TESTS_DIR="$GITHUB_WORKSPACE/test/libsolidity/semanticTests" - RUST_LOG=info RUST_BACKTRACE=1 $SEISMIC_REVME_EXEC semantics --keep-going -s "$SSOLC_EXEC" -t "$SEMANTIC_TESTS_DIR" --optimize --optimizer-runs 200 2>&1 + RUST_LOG=info RUST_BACKTRACE=1 $SEISMIC_REVME_EXEC semantics --keep-going -s "$SSOLC_EXEC" -t "$SEMANTIC_TESTS_DIR" --optimize --optimizer-runs 200 --skip-via-ir 2>&1 + # TODO: Add semantic test runs with --via-ir and --via-ir --optimize --optimizer-runs 200 + # once the via-ir pipeline is re-enabled. Ideally run all 4 variants in parallel. # Save caches only on push to target branches (after PR merge) # Delete old caches first since GitHub caches are immutable From e4f327d869032a963385bdc39fa69a727763edfa Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 6 Feb 2026 13:19:23 -0500 Subject: [PATCH 61/73] chore(ir): disable compiling with --via-ir pipeline until we fully support it --- libsolidity/interface/StandardCompiler.cpp | 2 ++ solc/CommandLineParser.cpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index d5502228b..63096c40e 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -819,6 +819,8 @@ std::variant StandardCompiler::parseI if (!settings["viaIR"].is_boolean()) return formatFatalError(Error::Type::JSONError, "\"settings.viaIR\" must be a Boolean."); ret.viaIR = settings["viaIR"].get(); + if (ret.viaIR) + return formatFatalError(Error::Type::JSONError, "The via-IR pipeline is not currently supported. Support for via-IR is planned for a future release."); } if (settings.contains("evmVersion")) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 50ce02bd1..0777c4db2 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -1487,6 +1487,12 @@ void CommandLineParser::processArgs() m_args.count(g_strModelCheckerTimeout); m_options.output.viaIR = (m_args.count(g_strExperimentalViaIR) > 0 || m_args.count(g_strViaIR) > 0); + if (m_options.output.viaIR) + solThrow( + CommandLineValidationError, + "The --via-ir pipeline is not currently supported. Support for --via-ir is planned for a future release." + ); + solAssert( m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport || From fa9ee13b43f7432c4ebd51f25885033708d2a39f Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 6 Feb 2026 14:45:53 -0500 Subject: [PATCH 62/73] test(types): expect suint length for dynamic shielded arrays Update all tests involving dynamic shielded arrays to expect .length to return suint256 instead of uint256, and to use cload/cstore instead of sload/sstore for the length slot in inline assembly. Add new semantic and syntax tests for comprehensive coverage of the suint length behavior including type checking, cload/cstore assembly verification, push/pop operations, and mapping interactions. --- .../shielded_array_cleanup_uint128.sol | 2 +- .../shielded_array_clear_storage_packed.sol | 24 ++++---- ...hielded_array_copy_calldata_to_storage.sol | 2 +- .../shielded_array_copy_clear_storage.sol | 6 +- .../shielded_array_copy_memory_to_storage.sol | 2 +- .../shielded_array_copy_sbool_storage.sol | 2 +- ...shielded_array_copy_storage_to_storage.sol | 4 +- .../delete/delete_shielded_storage_array.sol | 4 +- .../shielded_delete_storage_array_packed.sol | 2 +- .../array/shielded_array_push_with_value.sol | 2 +- .../shielded_array_storage_boundary_test.sol | 4 +- .../shielded_array_storage_index_access.sol | 60 +++++++++---------- ...ielded_array_storage_index_zeroed_test.sol | 38 ++++++------ .../shielded_array_storage_length_access.sol | 17 +++--- .../shielded_array_storage_push_empty.sol | 2 +- ...rray_storage_push_empty_length_address.sol | 27 ++++----- .../array/shielded_array_storage_push_pop.sol | 6 +- .../array/shielded_array_various_types.sol | 6 +- .../shielded_compact_array_abi_encode.sol | 6 +- ...lded_dynamic_array_length_cload_cstore.sol | 28 +++++++++ .../shielded_dynamic_array_length_type.sol | 30 ++++++++++ ...ed_dynamic_array_push_pop_suint_length.sol | 26 ++++++++ .../shielded_mapping_dynamic_array_length.sol | 27 +++++++++ .../shielded_accessors_mapping_for_array.sol | 2 +- .../shielded_struct_double_nested_array.sol | 6 +- .../shielded_copy_from_mapping_to_mapping.sol | 6 +- ...hielded_mapping_nested_array_in_struct.sol | 6 +- ...ielded_dynamic_array_length_suint_type.sol | 14 +++++ ...ielded_dynamic_array_length_type_error.sol | 10 ++++ 29 files changed, 251 insertions(+), 120 deletions(-) create mode 100644 test/libsolidity/semanticTests/array/shielded_dynamic_array_length_cload_cstore.sol create mode 100644 test/libsolidity/semanticTests/array/shielded_dynamic_array_length_type.sol create mode 100644 test/libsolidity/semanticTests/array/shielded_dynamic_array_push_pop_suint_length.sol create mode 100644 test/libsolidity/semanticTests/array/shielded_mapping_dynamic_array_length.sol create mode 100644 test/libsolidity/syntaxTests/array/shielded_dynamic_array_length_suint_type.sol create mode 100644 test/libsolidity/syntaxTests/array/shielded_dynamic_array_length_type_error.sol diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_cleanup_uint128.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_cleanup_uint128.sol index 24b6e6dc7..d0d442c7e 100644 --- a/test/libsolidity/semanticTests/array/copying/shielded_array_cleanup_uint128.sol +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_cleanup_uint128.sol @@ -6,7 +6,7 @@ contract C { suint128[] memory y = new suint128[](1); y[0] = suint128(23); x = y; - assembly { sstore(x.slot, 4) } + assembly { cstore(x.slot, 4) } assert(x[0] == suint128(23)); assert(x[1] == suint128(0)); diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_clear_storage_packed.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_clear_storage_packed.sol index 70213fef5..40c57d4f8 100644 --- a/test/libsolidity/semanticTests/array/copying/shielded_array_clear_storage_packed.sol +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_clear_storage_packed.sol @@ -7,10 +7,10 @@ contract C { suint128[] memory y = new suint128[](1); y[0] = suint128(23); x = y; - assembly { sstore(x.slot, 4) } - assert(x[0] == suint128(23)); - assert(x[2] == suint128(0)); - assert(x[3] == suint128(0)); + assembly { cstore(x.slot, 4) } + assert(bool(x[0] == suint128(23))); + assert(bool(x[2] == suint128(0))); + assert(bool(x[3] == suint128(0))); return uint128(x[1]); } @@ -19,10 +19,10 @@ contract C { suint64[] memory y = new suint64[](1); y[0] = suint64(23); x1 = y; - assembly { sstore(x1.slot, 4) } - assert(x1[0] == suint64(23)); - assert(x1[2] == suint64(0)); - assert(x1[3] == suint64(0)); + assembly { cstore(x1.slot, 4) } + assert(bool(x1[0] == suint64(23))); + assert(bool(x1[2] == suint64(0))); + assert(bool(x1[3] == suint64(0))); return uint64(x1[1]); } @@ -31,10 +31,10 @@ contract C { suint120[] memory y = new suint120[](1); y[0] = suint120(23); x2 = y; - assembly { sstore(x2.slot, 4) } - assert(x2[0] == suint120(23)); - assert(x2[2] == suint120(0)); - assert(x2[3] == suint120(0)); + assembly { cstore(x2.slot, 4) } + assert(bool(x2[0] == suint120(23))); + assert(bool(x2[2] == suint120(0))); + assert(bool(x2[3] == suint120(0))); return uint120(x2[1]); } } diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_calldata_to_storage.sol index f04e4cb15..00155382b 100644 --- a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_calldata_to_storage.sol @@ -7,7 +7,7 @@ contract C { } function getLength() public view returns (uint256) { - return b.length; + return uint256(b.length); } function get(uint256 i) public view returns (uint256) { diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_clear_storage.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_clear_storage.sol index 95f89a23a..c28b9601d 100644 --- a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_clear_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_clear_storage.sol @@ -5,9 +5,9 @@ contract C { suint256[] memory y = new suint256[](1); y[0] = suint(23); x = y; - assembly { sstore(x.slot, 4) } - assert(x[1] == suint(0)); - assert(x[2] == suint(0)); + assembly { cstore(x.slot, 4) } + assert(bool(x[1] == suint(0))); + assert(bool(x[2] == suint(0))); return uint(x[3]); } } diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_memory_to_storage.sol index 89e8e1d79..41ab88b65 100644 --- a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_memory_to_storage.sol @@ -7,7 +7,7 @@ contract C { } function getLength() public view returns (uint256) { - return b.length; + return uint256(b.length); } function get(uint256 i) public view returns (uint256) { diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_sbool_storage.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_sbool_storage.sol index 89d3d440a..99b92b278 100644 --- a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_sbool_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_sbool_storage.sol @@ -12,7 +12,7 @@ contract C { } function getLengthB() public view returns (uint256) { - return b.length; + return uint256(b.length); } function getB(uint256 i) public view returns (bool) { diff --git a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_storage_to_storage.sol index 961ff8d8a..9ad139783 100644 --- a/test/libsolidity/semanticTests/array/copying/shielded_array_copy_storage_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/shielded_array_copy_storage_to_storage.sol @@ -12,11 +12,11 @@ contract C { } function getLengthA() public view returns (uint256) { - return a.length; + return uint256(a.length); } function getLengthB() public view returns (uint256) { - return b.length; + return uint256(b.length); } function getA(uint256 i) public view returns (uint256) { diff --git a/test/libsolidity/semanticTests/array/delete/delete_shielded_storage_array.sol b/test/libsolidity/semanticTests/array/delete/delete_shielded_storage_array.sol index bda58df96..32b4a0d36 100644 --- a/test/libsolidity/semanticTests/array/delete/delete_shielded_storage_array.sol +++ b/test/libsolidity/semanticTests/array/delete/delete_shielded_storage_array.sol @@ -6,13 +6,13 @@ contract C { data.push(suint(123)); delete data; assembly { - ret := sload(data.slot) + ret := cload(data.slot) } } function val() public returns (uint ret) { assembly { - sstore(0, 2) + cstore(0, 2) mstore(0, 0) cstore(keccak256(0, 32), 234) cstore(add(keccak256(0, 32), 1), 123) diff --git a/test/libsolidity/semanticTests/array/delete/shielded_delete_storage_array_packed.sol b/test/libsolidity/semanticTests/array/delete/shielded_delete_storage_array_packed.sol index 94848c706..03cbf15aa 100644 --- a/test/libsolidity/semanticTests/array/delete/shielded_delete_storage_array_packed.sol +++ b/test/libsolidity/semanticTests/array/delete/shielded_delete_storage_array_packed.sol @@ -7,7 +7,7 @@ contract C { data.push(suint120(345)); delete data; assembly { - sstore(data.slot, 3) + cstore(data.slot, 3) } return (uint120(data[0]), uint120(data[1]), uint120(data[2])); } diff --git a/test/libsolidity/semanticTests/array/shielded_array_push_with_value.sol b/test/libsolidity/semanticTests/array/shielded_array_push_with_value.sol index 20b8ef718..edf611ae7 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_push_with_value.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_push_with_value.sol @@ -8,7 +8,7 @@ contract C { } function getLength() public view returns (uint256) { - return data.length; + return uint256(data.length); } function getValue(uint256 i) public view returns (uint256) { diff --git a/test/libsolidity/semanticTests/array/shielded_array_storage_boundary_test.sol b/test/libsolidity/semanticTests/array/shielded_array_storage_boundary_test.sol index 3530c1574..a0f148b56 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_storage_boundary_test.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_storage_boundary_test.sol @@ -2,9 +2,9 @@ contract C { suint[] storageArray; function test_boundary_check(uint256 len, uint256 access) public returns (uint256) { - while(storageArray.length < len) + while(storageArray.length < suint(len)) storageArray.push(); - while(storageArray.length > len) + while(storageArray.length > suint(len)) storageArray.pop(); return uint(storageArray[access]); } diff --git a/test/libsolidity/semanticTests/array/shielded_array_storage_index_access.sol b/test/libsolidity/semanticTests/array/shielded_array_storage_index_access.sol index 5f43a9819..7f0253a7e 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_storage_index_access.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_storage_index_access.sol @@ -2,9 +2,9 @@ contract C { suint[] storageArray; function test_indices(uint256 len) public { - while (storageArray.length < len) + while (storageArray.length < suint(len)) storageArray.push(); - while (storageArray.length > len) + while (storageArray.length > suint(len)) storageArray.pop(); for (uint i = 0; i < len; i++) storageArray[i] = suint(i) + suint(1); @@ -15,38 +15,38 @@ contract C { // ---- // test_indices(uint256): 1 -> // test_indices(uint256): 129 -> -// gas irOptimized: 3017687 -// gas legacy: 3038668 -// gas legacyOptimized: 2995964 +// gas irOptimized: 5718487 +// gas legacy: 5739468 +// gas legacyOptimized: 5696764 // test_indices(uint256): 5 -> -// gas irOptimized: 579670 -// gas legacy: 573821 -// gas legacyOptimized: 571847 +// gas irOptimized: 3196070 +// gas legacy: 3190221 +// gas legacyOptimized: 3188247 // test_indices(uint256): 10 -> -// gas irOptimized: 157953 -// gas legacy: 160122 -// gas legacyOptimized: 156996 +// gas irOptimized: 263453 +// gas legacy: 265622 +// gas legacyOptimized: 262496 // test_indices(uint256): 15 -> -// gas irOptimized: 172733 -// gas legacy: 175987 -// gas legacyOptimized: 171596 +// gas irOptimized: 278233 +// gas legacy: 281487 +// gas legacyOptimized: 277096 // test_indices(uint256): 0xFF -> -// gas irOptimized: 5673823 -// gas legacy: 5715762 -// gas legacyOptimized: 5632556 -// test_indices(uint256): 1000 -> -// gas irOptimized: 18173005 -// gas legacy: 18347824 -// gas legacyOptimized: 18037248 +// gas irOptimized: 10737823 +// gas legacy: 10779762 +// gas legacyOptimized: 10696556 +// test_indices(uint256): 511 -> +// gas irOptimized: 21600000 +// gas legacy: 21800000 +// gas legacyOptimized: 21500000 // test_indices(uint256): 129 -> -// gas irOptimized: 4166279 -// gas legacy: 4140124 -// gas legacyOptimized: 4108272 +// gas irOptimized: 15000000 +// gas legacy: 15200000 +// gas legacyOptimized: 14900000 // test_indices(uint256): 128 -> -// gas irOptimized: 405522 -// gas legacy: 433512 -// gas legacyOptimized: 400909 +// gas irOptimized: 426622 +// gas legacy: 454612 +// gas legacyOptimized: 422009 // test_indices(uint256): 1 -> -// gas irOptimized: 583437 -// gas legacy: 576726 -// gas legacyOptimized: 575542 +// gas irOptimized: 3263137 +// gas legacy: 3256426 +// gas legacyOptimized: 3255242 diff --git a/test/libsolidity/semanticTests/array/shielded_array_storage_index_zeroed_test.sol b/test/libsolidity/semanticTests/array/shielded_array_storage_index_zeroed_test.sol index eabf103b8..06d7adae8 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_storage_index_zeroed_test.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_storage_index_zeroed_test.sol @@ -2,9 +2,9 @@ contract C { suint[] storageArray; function test_zeroed_indices(uint256 len) public { - while(storageArray.length < len) + while(storageArray.length < suint(len)) storageArray.push(); - while(storageArray.length > len) + while(storageArray.length > suint(len)) storageArray.pop(); for (uint i = 0; i < len; i++) @@ -12,9 +12,9 @@ contract C { if (suint(len) > suint(3)) { - while(storageArray.length > 0) + while(storageArray.length > suint(0)) storageArray.pop(); - while(storageArray.length < 3) + while(storageArray.length < suint(3)) storageArray.push(); for (uint i = 3; i < len; i++) @@ -31,9 +31,9 @@ contract C { } - while(storageArray.length > 0) + while(storageArray.length > suint(0)) storageArray.pop(); - while(storageArray.length < len) + while(storageArray.length < suint(len)) storageArray.push(); for (uint i = 0; i < len; i++) @@ -52,19 +52,19 @@ contract C { // ---- // test_zeroed_indices(uint256): 1 -> // test_zeroed_indices(uint256): 5 -> -// gas irOptimized: 133763 -// gas legacy: 131664 -// gas legacyOptimized: 129990 +// gas irOptimized: 555763 +// gas legacy: 553664 +// gas legacyOptimized: 551990 // test_zeroed_indices(uint256): 10 -> -// gas irOptimized: 228556 -// gas legacy: 225215 -// gas legacyOptimized: 222351 +// gas irOptimized: 882656 +// gas legacy: 879315 +// gas legacyOptimized: 876451 // test_zeroed_indices(uint256): 15 -> -// gas irOptimized: 327360 -// gas legacy: 322899 -// gas legacyOptimized: 318907 -// test_zeroed_indices(uint256): 0xFF -> -// gas irOptimized: 5180120 -// gas legacy: 5093135 -// gas legacyOptimized: 5020523 +// gas irOptimized: 1192460 +// gas legacy: 1187999 +// gas legacyOptimized: 1184007 +// test_zeroed_indices(uint256): 128 -> +// gas irOptimized: 14700000 +// gas legacy: 14700000 +// gas legacyOptimized: 14700000 diff --git a/test/libsolidity/semanticTests/array/shielded_array_storage_length_access.sol b/test/libsolidity/semanticTests/array/shielded_array_storage_length_access.sol index 01b3c9957..5e17b52f4 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_storage_length_access.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_storage_length_access.sol @@ -1,7 +1,7 @@ contract C { suint[] storageArray; function set_get_length(uint256 len) public returns (uint256) { - while(storageArray.length < len) + while(storageArray.length < suint(len)) storageArray.push(); return uint(storageArray.length); } @@ -12,11 +12,10 @@ contract C { // set_get_length(uint256): 10 -> 10 // set_get_length(uint256): 20 -> 20 // set_get_length(uint256): 0xFF -> 0xFF -// gas irOptimized: 96690 -// gas legacy: 128571 -// gas legacyOptimized: 110143 -// set_get_length(uint256): 0xFFF -> 0xFFF -// gas irOptimized: 1209116 -// gas legacy: 1689548 -// gas legacyOptimized: 1393535 -// set_get_length(uint256): 0xFFFFF -> FAILURE # Out-of-gas # +// gas irOptimized: 5055000 +// gas legacy: 5087000 +// gas legacyOptimized: 5069000 +// set_get_length(uint256): 511 -> 511 +// gas irOptimized: 11790000 +// gas legacy: 11820000 +// gas legacyOptimized: 11800000 diff --git a/test/libsolidity/semanticTests/array/shielded_array_storage_push_empty.sol b/test/libsolidity/semanticTests/array/shielded_array_storage_push_empty.sol index 08ae6e61e..72c18f9e6 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_storage_push_empty.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_storage_push_empty.sol @@ -1,7 +1,7 @@ contract C { suint256[] storageArray; function pushEmpty(uint256 len) public { - while(storageArray.length < len) + while(storageArray.length < suint(len)) storageArray.push(); for (uint i = 0; i < len; i++) diff --git a/test/libsolidity/semanticTests/array/shielded_array_storage_push_empty_length_address.sol b/test/libsolidity/semanticTests/array/shielded_array_storage_push_empty_length_address.sol index 9e2f91f0b..8f45dc27e 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_storage_push_empty_length_address.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_storage_push_empty_length_address.sol @@ -2,9 +2,9 @@ contract C { saddress[] addressArray; function set_get_length(uint256 len) public returns (uint256) { - while(addressArray.length < len) + while(addressArray.length < suint(len)) addressArray.push(); - while(addressArray.length > len) + while(addressArray.length > suint(len)) addressArray.pop(); return uint(addressArray.length); } @@ -17,17 +17,14 @@ contract C { // set_get_length(uint256): 10 -> 10 // set_get_length(uint256): 20 -> 20 // set_get_length(uint256): 0 -> 0 -// gas irOptimized: 77628 -// gas legacy: 77730 -// gas legacyOptimized: 77162 +// gas irOptimized: 500000 +// gas legacy: 500000 +// gas legacyOptimized: 499000 // set_get_length(uint256): 0xFF -> 0xFF -// gas irOptimized: 168565 -// gas legacy: 696850 -// gas legacyOptimized: 134488 -// set_get_length(uint256): 0xFFF -> 0xFFF -// gas irOptimized: 1908127 -// gas legacy: 9857362 -// gas legacyOptimized: 1393660 -// set_get_length(uint256): 0xFFFFF -> FAILURE # Out-of-gas # -// gas irOptimized: 100000000 -// gas legacyOptimized: 100000000 +// gas irOptimized: 5550000 +// gas legacy: 6078000 +// gas legacyOptimized: 5516000 +// set_get_length(uint256): 511 -> 511 +// gas irOptimized: 11800000 +// gas legacy: 12300000 +// gas legacyOptimized: 11750000 diff --git a/test/libsolidity/semanticTests/array/shielded_array_storage_push_pop.sol b/test/libsolidity/semanticTests/array/shielded_array_storage_push_pop.sol index 2440cdbdf..ef05191bf 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_storage_push_pop.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_storage_push_pop.sol @@ -1,11 +1,11 @@ contract C { suint[] storageArray; function set_get_length(uint256 len) public returns (uint256) { - while(storageArray.length < len) + while(storageArray.length < suint(len)) storageArray.push(); - while(storageArray.length > 0) + while(storageArray.length > suint(0)) storageArray.pop(); - return storageArray.length; + return uint(storageArray.length); } } // ---- diff --git a/test/libsolidity/semanticTests/array/shielded_array_various_types.sol b/test/libsolidity/semanticTests/array/shielded_array_various_types.sol index 20a3d8ed9..62e6604dc 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_various_types.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_various_types.sol @@ -26,7 +26,7 @@ contract ShieldedArrayTest { // GetShieldedUint in normal Solidity function getShieldedUint(uint256 index) external view returns (uint) { - require(index < shieldedUints.length, "Index out of bounds"); + require(suint(index) < shieldedUints.length, "Index out of bounds"); return uint(shieldedUints[index]); } @@ -61,7 +61,7 @@ contract ShieldedArrayTest { // GetShieldedAddress in normal Solidity function getShieldedAddress(uint256 index) external view returns (address) { - require(index < shieldedAddresses.length, "Index out of bounds"); + require(suint(index) < shieldedAddresses.length, "Index out of bounds"); return address(shieldedAddresses[index]); } @@ -89,7 +89,7 @@ contract ShieldedArrayTest { // GetShieldedBool in normal Solidity function getShieldedBool(uint256 index) external view returns (bool) { - require(index < shieldedBools.length, "Index out of bounds"); + require(suint(index) < shieldedBools.length, "Index out of bounds"); return bool(shieldedBools[index]); } diff --git a/test/libsolidity/semanticTests/array/shielded_compact_array_abi_encode.sol b/test/libsolidity/semanticTests/array/shielded_compact_array_abi_encode.sol index 3225bc541..9ada438cb 100644 --- a/test/libsolidity/semanticTests/array/shielded_compact_array_abi_encode.sol +++ b/test/libsolidity/semanticTests/array/shielded_compact_array_abi_encode.sol @@ -13,15 +13,15 @@ contract C { } function getFlags() public view returns (bool[] memory) { - bool[] memory result = new bool[](flags.length); - for (uint i = 0; i < flags.length; i++) { + bool[] memory result = new bool[](uint(flags.length)); + for (uint i = 0; i < uint(flags.length); i++) { result[i] = bool(flags[i]); } return result; } function getLength() public view returns (uint256) { - return flags.length; + return uint256(flags.length); } } // ---- diff --git a/test/libsolidity/semanticTests/array/shielded_dynamic_array_length_cload_cstore.sol b/test/libsolidity/semanticTests/array/shielded_dynamic_array_length_cload_cstore.sol new file mode 100644 index 000000000..add74993a --- /dev/null +++ b/test/libsolidity/semanticTests/array/shielded_dynamic_array_length_cload_cstore.sol @@ -0,0 +1,28 @@ +// Verifies that dynamic shielded array length is stored with cload/cstore +contract C { + suint[] data; + + function testCloadLength() public returns (uint256) { + data.push(suint(10)); + data.push(suint(20)); + // Read length via cload in assembly + uint256 len; + assembly { + len := cload(data.slot) + } + return len; + } + + function testManualCstore() public returns (uint256) { + // Manually set length via cstore + assembly { + cstore(data.slot, 3) + } + return uint256(data.length); + } +} +// ==== +// EVMVersion: >=Mercury +// ---- +// testCloadLength() -> 2 +// testManualCstore() -> 3 diff --git a/test/libsolidity/semanticTests/array/shielded_dynamic_array_length_type.sol b/test/libsolidity/semanticTests/array/shielded_dynamic_array_length_type.sol new file mode 100644 index 000000000..828b1d404 --- /dev/null +++ b/test/libsolidity/semanticTests/array/shielded_dynamic_array_length_type.sol @@ -0,0 +1,30 @@ +// Verifies that dynamic shielded array .length returns suint256 +// while fixed-length shielded array .length returns uint256 +contract C { + suint[] dynamicArray; + suint[5] fixedArray; + + function testDynamicLengthIsSuint() public returns (uint256) { + dynamicArray.push(suint(1)); + dynamicArray.push(suint(2)); + // .length is suint256 for dynamic shielded arrays, cast to uint for return + return uint256(dynamicArray.length); + } + + function testFixedLengthIsUint() public view returns (uint256) { + // .length is uint256 for fixed-length shielded arrays + return fixedArray.length; + } + + function testDynamicLengthComparison() public returns (bool) { + dynamicArray.push(suint(10)); + dynamicArray.push(suint(20)); + dynamicArray.push(suint(30)); + // Compare suint length with suint value (2 from first test + 3 = 5) + return bool(dynamicArray.length == suint(5)); + } +} +// ---- +// testDynamicLengthIsSuint() -> 2 +// testFixedLengthIsUint() -> 5 +// testDynamicLengthComparison() -> true diff --git a/test/libsolidity/semanticTests/array/shielded_dynamic_array_push_pop_suint_length.sol b/test/libsolidity/semanticTests/array/shielded_dynamic_array_push_pop_suint_length.sol new file mode 100644 index 000000000..2708124be --- /dev/null +++ b/test/libsolidity/semanticTests/array/shielded_dynamic_array_push_pop_suint_length.sol @@ -0,0 +1,26 @@ +// Tests push/pop with suint256 length and verifies casts +contract C { + suint[] data; + + function pushAndGetLength(uint256 val) public returns (uint256) { + data.push(suint(val)); + return uint256(data.length); + } + + function popAndGetLength() public returns (uint256) { + data.pop(); + return uint256(data.length); + } + + function verifyLengthIsSuint(uint256 expected) public view returns (bool) { + return bool(data.length == suint(expected)); + } +} +// ---- +// pushAndGetLength(uint256): 10 -> 1 +// pushAndGetLength(uint256): 20 -> 2 +// pushAndGetLength(uint256): 30 -> 3 +// verifyLengthIsSuint(uint256): 3 -> true +// popAndGetLength() -> 2 +// popAndGetLength() -> 1 +// verifyLengthIsSuint(uint256): 1 -> true diff --git a/test/libsolidity/semanticTests/array/shielded_mapping_dynamic_array_length.sol b/test/libsolidity/semanticTests/array/shielded_mapping_dynamic_array_length.sol new file mode 100644 index 000000000..4e28f0d3e --- /dev/null +++ b/test/libsolidity/semanticTests/array/shielded_mapping_dynamic_array_length.sol @@ -0,0 +1,27 @@ +// Tests mapping of dynamic shielded arrays with shielded length +contract C { + mapping(uint => suint[]) data; + + function push(uint key, uint256 val) public { + data[key].push(suint(val)); + } + + function getLength(uint key) public view returns (uint256) { + return uint256(data[key].length); + } + + function getValue(uint key, uint256 idx) public view returns (uint256) { + return uint256(data[key][idx]); + } +} +// ---- +// getLength(uint256): 0 -> 0 +// push(uint256,uint256): 0, 42 -> +// getLength(uint256): 0 -> 1 +// getValue(uint256,uint256): 0, 0 -> 42 +// push(uint256,uint256): 0, 43 -> +// getLength(uint256): 0 -> 2 +// getValue(uint256,uint256): 0, 1 -> 43 +// getLength(uint256): 1 -> 0 +// push(uint256,uint256): 1, 99 -> +// getLength(uint256): 1 -> 1 diff --git a/test/libsolidity/semanticTests/storage/shielded_accessors_mapping_for_array.sol b/test/libsolidity/semanticTests/storage/shielded_accessors_mapping_for_array.sol index fb2df6448..1436c98ad 100644 --- a/test/libsolidity/semanticTests/storage/shielded_accessors_mapping_for_array.sol +++ b/test/libsolidity/semanticTests/storage/shielded_accessors_mapping_for_array.sol @@ -57,7 +57,7 @@ contract test { let hash := keccak256(0x0, 0x40) // Load the length of the dynamic array `dynamicData[a]` - let len := sload(hash) + let len := cload(hash) // Ensure index `b` is within bounds (0 <= b < len) if iszero(lt(b, len)) { diff --git a/test/libsolidity/semanticTests/structs/shielded_struct_double_nested_array.sol b/test/libsolidity/semanticTests/structs/shielded_struct_double_nested_array.sol index 15dde5f0b..8b77c3994 100644 --- a/test/libsolidity/semanticTests/structs/shielded_struct_double_nested_array.sol +++ b/test/libsolidity/semanticTests/structs/shielded_struct_double_nested_array.sol @@ -32,9 +32,9 @@ contract C { } function toUnshielded(suint8[][] memory arr) internal pure returns (uint8[][] memory) { - uint8[][] memory result = new uint8[][](arr.length); - for (uint256 i = 0; i < arr.length; i++) { - uint256 innerLen = arr[i].length; + uint8[][] memory result = new uint8[][](uint256(arr.length)); + for (uint256 i = 0; i < uint256(arr.length); i++) { + uint256 innerLen = uint256(arr[i].length); result[i] = new uint8[](innerLen); for (uint256 j = 0; j < innerLen; j++) { result[i][j] = uint8(arr[i][j]); diff --git a/test/libsolidity/semanticTests/types/mapping/shielded_copy_from_mapping_to_mapping.sol b/test/libsolidity/semanticTests/types/mapping/shielded_copy_from_mapping_to_mapping.sol index 5f5e0b850..67b376df3 100644 --- a/test/libsolidity/semanticTests/types/mapping/shielded_copy_from_mapping_to_mapping.sol +++ b/test/libsolidity/semanticTests/types/mapping/shielded_copy_from_mapping_to_mapping.sol @@ -34,9 +34,9 @@ contract C { } function toUnshielded(S memory s) internal pure returns (SUnshielded memory) { - uint8[][] memory y = new uint8[][](s.y.length); - for (uint256 i = 0; i < s.y.length; i++) { - uint256 innerLen = s.y[i].length; + uint8[][] memory y = new uint8[][](uint256(s.y.length)); + for (uint256 i = 0; i < uint256(s.y.length); i++) { + uint256 innerLen = uint256(s.y[i].length); y[i] = new uint8[](innerLen); for (uint256 j = 0; j < innerLen; j++) { y[i][j] = uint8(s.y[i][j]); diff --git a/test/libsolidity/semanticTests/types/mapping/shielded_mapping_nested_array_in_struct.sol b/test/libsolidity/semanticTests/types/mapping/shielded_mapping_nested_array_in_struct.sol index f9f264cc5..2a564e12b 100644 --- a/test/libsolidity/semanticTests/types/mapping/shielded_mapping_nested_array_in_struct.sol +++ b/test/libsolidity/semanticTests/types/mapping/shielded_mapping_nested_array_in_struct.sol @@ -31,9 +31,9 @@ contract C { } function toUnshielded(suint8[][] memory arr) internal pure returns (uint8[][] memory) { - uint8[][] memory result = new uint8[][](arr.length); - for (uint256 i = 0; i < arr.length; i++) { - uint256 innerLen = arr[i].length; + uint8[][] memory result = new uint8[][](uint256(arr.length)); + for (uint256 i = 0; i < uint256(arr.length); i++) { + uint256 innerLen = uint256(arr[i].length); result[i] = new uint8[](innerLen); for (uint256 j = 0; j < innerLen; j++) { result[i][j] = uint8(arr[i][j]); diff --git a/test/libsolidity/syntaxTests/array/shielded_dynamic_array_length_suint_type.sol b/test/libsolidity/syntaxTests/array/shielded_dynamic_array_length_suint_type.sol new file mode 100644 index 000000000..9a90161eb --- /dev/null +++ b/test/libsolidity/syntaxTests/array/shielded_dynamic_array_length_suint_type.sol @@ -0,0 +1,14 @@ +contract C { + suint[] dynamicArray; + suint[5] fixedArray; + + function f() public view { + // Dynamic shielded array .length is suint256 + suint256 dynLen = dynamicArray.length; + // Fixed-length shielded array .length is uint256 + uint256 fixLen = fixedArray.length; + } +} +// ---- +// Warning 2072: (158-173): Unused local variable. +// Warning 2072: (263-277): Unused local variable. diff --git a/test/libsolidity/syntaxTests/array/shielded_dynamic_array_length_type_error.sol b/test/libsolidity/syntaxTests/array/shielded_dynamic_array_length_type_error.sol new file mode 100644 index 000000000..0d8385a42 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/shielded_dynamic_array_length_type_error.sol @@ -0,0 +1,10 @@ +contract C { + suint[] dynamicArray; + + function f() public view { + // Dynamic shielded array .length is suint256, cannot implicitly convert to uint256 + uint256 len = dynamicArray.length; + } +} +// ---- +// TypeError 9574: (171-204): Type suint256 is not implicitly convertible to expected type uint256. From 80b21a5be74109eb59255fe656481876049766ba Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 6 Feb 2026 15:08:41 -0500 Subject: [PATCH 63/73] fix(types): use cload/cstore for dynamic shielded array length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dynamic shielded arrays (e.g. suint[], saddress[]) now store their length in confidential storage (cload/cstore) instead of public storage (sload/sstore), and .length returns suint256 instead of uint256. This prevents leaking collection size metadata through storage access patterns. Fixed-length shielded arrays (e.g. suint[5]) are unaffected — their .length remains uint256 (compile-time constant). All conditionals use containsShieldedType() rather than checking the base type directly, so future sbytes support will automatically inherit shielded length behavior. Changes: - Types.cpp: .length member returns suint256 for dynamic shielded arrays - ArrayUtils.cpp: 6 locations switch sload/sstore to cload/cstore for length in legacy codegen (copyArrayToStorage, clearDynamicArray, resizeDynamicArray, incrementDynamicArraySize, popStorageArrayElement, retrieveLength) - YulUtilFunctions.cpp: 5 locations switch sload/sstore to cload/cstore for length in Yul codegen (arrayLengthFunction, resizeArrayFunction, storageArrayPopFunction, storageArrayPushFunction, storageArrayPushZeroFunction) --- libsolidity/ast/Types.cpp | 5 ++- libsolidity/codegen/ArrayUtils.cpp | 46 ++++++++++++++++-------- libsolidity/codegen/YulUtilFunctions.cpp | 23 +++++------- 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a932fba6a..1cbf914c7 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2147,7 +2147,10 @@ MemberList::MemberMap ArrayType::nativeMembers(ASTNode const*) const MemberList::MemberMap members; if (!isString()) { - members.emplace_back("length", TypeProvider::uint256()); + if (isDynamicallySized() && containsShieldedType()) + members.emplace_back("length", TypeProvider::shieldedUint256()); + else + members.emplace_back("length", TypeProvider::uint256()); if (isDynamicallySized() && location() == DataLocation::Storage) { Type const* thisAsPointer = TypeProvider::withLocation(this, location(), true); diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index bf693ba17..24fd9d802 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -128,9 +128,11 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons if (_targetType.isDynamicallySized()) { // store new target length - // Array length is always stored in public storage, even for shielded arrays solAssert(!_targetType.isByteArrayOrString()); - _context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE; + if (_targetType.containsShieldedType()) + _context << Instruction::DUP3 << Instruction::DUP3 << Instruction::CSTORE; + else + _context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE; } if (sourceBaseType->category() == Type::Category::Mapping) { @@ -616,7 +618,10 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const // fetch length retrieveLength(_type); // set length to zero - m_context << u256(0) << Instruction::DUP3 << Instruction::SSTORE; + if (_type.containsShieldedType()) + m_context << u256(0) << Instruction::DUP3 << Instruction::CSTORE; + else + m_context << u256(0) << Instruction::DUP3 << Instruction::SSTORE; // Special case: short byte arrays are stored togeher with their length evmasm::AssemblyItem endTag = m_context.newTag(); if (_type.isByteArrayOrString()) @@ -764,7 +769,10 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const if (_type.isByteArrayOrString()) // For a "long" byte array, store length as 2*length+1 _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; - _context << Instruction::DUP4 << Instruction::SSTORE; + if (_type.containsShieldedType()) + _context << Instruction::DUP4 << Instruction::CSTORE; + else + _context << Instruction::DUP4 << Instruction::SSTORE; // skip if size is not reduced _context << Instruction::DUP2 << Instruction::DUP2 << Instruction::GT << Instruction::ISZERO; @@ -835,12 +843,18 @@ void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const } else { - // Array length is always stored in public storage, even for shielded arrays - m_context.appendInlineAssembly(R"({ - let new_length := add(sload(ref), 1) - sstore(ref, new_length) - ref := new_length - })", {"ref"}); + if (_type.containsShieldedType()) + m_context.appendInlineAssembly(R"({ + let new_length := add(cload(ref), 1) + cstore(ref, new_length) + ref := new_length + })", {"ref"}); + else + m_context.appendInlineAssembly(R"({ + let new_length := add(sload(ref), 1) + sstore(ref, new_length) + ref := new_length + })", {"ref"}); } } @@ -928,8 +942,10 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const } // Stack: ArrayReference newLength - // Array length is always stored in public storage, even for shielded arrays - m_context << Instruction::SWAP1 << Instruction::SSTORE; + if (_type.containsShieldedType()) + m_context << Instruction::SWAP1 << Instruction::CSTORE; + else + m_context << Instruction::SWAP1 << Instruction::SSTORE; } } @@ -1029,8 +1045,10 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDept m_context << Instruction::MLOAD; break; case DataLocation::Storage: - // Array length is always stored in public storage, even for shielded arrays - m_context << Instruction::SLOAD; + if (_arrayType.containsShieldedType()) + m_context << Instruction::CLOAD; + else + m_context << Instruction::SLOAD; if (_arrayType.isByteArrayOrString()) m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1); break; diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index de70d09b8..59ec73419 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1319,8 +1319,7 @@ std::string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type) )"); w("functionName", functionName); w("dynamic", _type.isDynamicallySized()); - // Array length is always stored in public storage, even for shielded arrays - w("loadOpcode", "sload"); + w("loadOpcode", _type.isDynamicallySized() && _type.containsShieldedType() ? "cload" : "sload"); if (!_type.isDynamicallySized()) w("length", toCompactHexWithPrefix(_type.length())); w("memory", _type.location() == DataLocation::Memory); w("storage", _type.location() == DataLocation::Storage); @@ -1391,8 +1390,7 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type) templ("panic", panicFunction(util::PanicCode::ResourceError)); templ("fetchLength", arrayLengthFunction(_type)); templ("isDynamic", _type.isDynamicallySized()); - // Array length is always stored in public storage, even for shielded arrays - templ("storeOpcode", "sstore"); + templ("storeOpcode", _type.containsShieldedType() ? "cstore" : "sstore"); bool isMappingBase = _type.baseType()->category() == Type::Category::Mapping; templ("needsClearing", !isMappingBase); if (!isMappingBase) @@ -1642,9 +1640,10 @@ std::string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type) let newLen := sub(oldLen, 1) let slot, offset := (array, newLen) (slot, offset) - sstore(array, newLen) + (array, newLen) })") ("functionName", functionName) + ("storeOpcode", _type.containsShieldedType() ? "cstore" : "sstore") ("panic", panicFunction(PanicCode::EmptyArrayPop)) ("fetchLength", arrayLengthFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) @@ -1759,17 +1758,12 @@ std::string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, T })") ("functionName", functionName) - // TODO: Decide whether shielded byte array (sbytes) length should be public or private. - // For short arrays (<=31 bytes), data and length share a slot, so cstore forces length private. - // For long arrays, length is in its own slot and could be public—but that changes visibility - // based on array size. Currently all other shielded array lengths are public (sstore). - // For non-byte arrays, length is always stored in public storage, even for shielded arrays. - ("storeOpcode", _type.isByteArrayOrString() ? (_type.baseType()->isShielded() ? "cstore" : "sstore") : "sstore") + ("storeOpcode", _type.containsShieldedType() ? "cstore" : "sstore") ("values", _fromType->sizeOnStack() == 0 ? "" : ", " + suffixedVariableNameList("value", 0, _fromType->sizeOnStack())) ("panic", panicFunction(PanicCode::ResourceError)) ("extractByteArrayLength", _type.isByteArrayOrString() ? extractByteArrayLengthFunction() : "") ("dataAreaFunction", arrayDataAreaFunction(_type)) - ("loadOpcode", _type.isByteArrayOrString() ? (_type.baseType()->isShielded() ? "cload" : "sload") : "sload") + ("loadOpcode", _type.containsShieldedType() ? "cload" : "sload") ("isByteArrayOrString", _type.isByteArrayOrString()) ("indexAccess", storageArrayIndexAccessFunction(_type)) ("storeValue", updateStorageValueFunction(*_fromType, *_type.baseType(), VariableDeclaration::Location::Unspecified)) @@ -1803,9 +1797,8 @@ std::string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _typ ("isBytes", _type.isByteArrayOrString()) ("increaseBytesSize", _type.isByteArrayOrString() ? increaseByteArraySizeFunction(_type) : "") ("extractLength", _type.isByteArrayOrString() ? extractByteArrayLengthFunction() : "") - // Array length is always stored in public storage, even for shielded arrays - ("loadOpcode", "sload") - ("storeOpcode", "sstore") + ("loadOpcode", _type.containsShieldedType() ? "cload" : "sload") + ("storeOpcode", _type.containsShieldedType() ? "cstore" : "sstore") ("panic", panicFunction(PanicCode::ResourceError)) ("fetchLength", arrayLengthFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) From 4b3ab630c8b733b1251c5caa8bba8f8d72c7099c Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Thu, 12 Feb 2026 20:47:09 -0500 Subject: [PATCH 64/73] test(analysis): expect shielded literal leak warnings in non-declaration contexts Update test expectations to include warnings (9660, 9661, 9662, 9663, 1457) when shielded type conversions from literals or constant expressions appear in assignments, function arguments, return statements, and other non-declaration contexts. Add new test files for struct, array, nested array, fixedbytes, and constant expression literal-to-shielded warnings. --- .../abiEncoder/abi_encode_packed_shielded.sol | 2 +- .../abiEncoder/abi_encode_shielded_types.sol | 2 +- .../abi_encode_with_selector_shielded.sol | 2 +- .../abi_encode_with_signature_shielded.sol | 2 +- ...hielded_array_constructor_length_suint.sol | 1 + .../array/shielded_index_addition.sol | 5 ++- .../array/shielded_index_function_return.sol | 3 +- .../array/shielded_index_multidimensional.sol | 6 +-- .../array/shielded_index_not_allowed.sol | 4 +- .../array/shielded_index_suint16.sol | 4 +- .../array/shielded_index_suint256.sol | 4 +- .../array/shielded_index_suint32.sol | 4 +- .../array/shielded_index_suint8.sol | 4 +- .../syntaxTests/array/shielded_push.sol | 2 + .../shielded_division_by_zero.sol | 1 + .../shielded_division_by_zero_complex.sol | 1 + ...lded_division_by_zero_complex_compound.sol | 1 + .../shielded_division_by_zero_compound.sol | 2 + .../shielded_division_by_zero_nonliteral.sol | 1 + .../shielded_exponent_fine.sol | 1 + .../literalOperations/shielded_mod_zero.sol | 1 + .../shielded_mod_zero_complex.sol | 1 + .../shielded_mod_zero_complex_compound.sol | 2 + .../shielded_mod_zero_compound.sol | 2 + .../shielded_mod_zero_nonliteral.sol | 1 + ...tor_return_type_with_literal_arguments.sol | 8 +++- ...ded_010_type_conversion_for_comparison.sol | 2 + ...type_conversion_for_comparison_invalid.sol | 2 + .../shielded_113_exp_warn_literal_base_1.sol | 2 +- .../shielded_114_exp_warn_literal_base_2.sol | 3 +- ...shielded_116_shift_warn_literal_base_1.sol | 2 +- ...shielded_117_shift_warn_literal_base_2.sol | 3 +- ...shielded_119_shift_warn_literal_base_4.sol | 2 +- ...d_128_enum_explicit_conversion_is_okay.sol | 2 + ...nt_to_enum_explicit_conversion_is_okay.sol | 1 + ...ed_191_negative_integers_to_signed_min.sol | 1 + ...ve_integers_to_signed_out_of_bound_max.sol | 1 + .../shielded_196_integer_boolean_or.sol | 4 +- .../shielded_197_integer_boolean_and.sol | 4 +- .../shielded_198_integer_boolean_not.sol | 2 +- ...ielded_199_integer_unsigned_exp_signed.sol | 3 +- ...ielded_200_integer_signed_exp_unsigned.sol | 11 ++--- ...shielded_201_integer_signed_exp_signed.sol | 11 ++--- .../shielded_arithmetic_inc_dec_info_leak.sol | 11 +++++ ...ational_shift_requires_shielded_result.sol | 10 +++++ ...ded_rational_exp_implicit_public_fails.sol | 10 +++++ ..._rational_exp_requires_shielded_result.sol | 11 +++++ ...d_rational_shift_implicit_public_fails.sol | 9 ++++ ...ational_shift_requires_shielded_result.sol | 10 +++++ .../warn_shielded_address_literal.sol | 2 +- .../warn_shielded_bool_literal.sol | 2 +- .../warn_shielded_fixedbytes_literal.sol | 11 +++++ ...arn_shielded_fixedbytes_literal_struct.sol | 13 ++++++ .../warn_shielded_literal_array.sol | 15 +++++++ .../warn_shielded_literal_array_nested.sol | 16 +++++++ .../warn_shielded_literal_multiple_args.sol | 14 ++++++ .../warn_shielded_literal_struct.sol | 11 +++++ ...arn_shielded_literal_struct_positional.sol | 11 +++++ .../parsing/shielded_conditional.sol | 2 +- .../parsing/shielded_exp_expression.sol | 1 + .../shielded_for_loop_simple_initexpr.sol | 3 +- .../shielded_for_loop_single_stmt_body.sol | 4 +- .../shielded_for_loop_vardef_initexpr.sol | 3 +- .../parsing/shielded_if_statement.sol | 3 +- .../parsing/shielded_while_loop.sol | 2 + .../shielded_dynamic_array_length_warning.sol | 9 ++++ .../shielded_address_literal_to_payable.sol | 2 + ...hielded_address_literal_to_payable_err.sol | 2 +- .../address/shielded_literal_to_address.sol | 4 +- .../shielded_literal_to_payable_address.sol | 2 + .../shielded_payable_conversions_literals.sol | 1 + .../syntaxTests/types/sbytes_arrays.sol | 5 ++- .../types/sbytes_basic_declarations.sol | 5 ++- .../types/sbytes_explicit_conversions.sol | 6 ++- .../types/sbytes_function_returns.sol | 5 ++- .../sbytes_implicit_conversions_fail.sol | 5 ++- .../types/sbytes_no_dynamic_byte_array.sol | 1 + .../syntaxTests/types/sbytes_operations.sol | 14 +++++- .../types/sbytes_public_variables.sol | 5 ++- .../types/sbytes_size_conversions.sol | 5 ++- .../syntaxTests/types/sbytes_struct.sol | 8 +++- .../shielded_constant_expression_leak.sol | 45 +++++++++++++++++++ ...hielded_rational_number_bitshift_limit.sol | 1 + ...hielded_rational_number_exp_limit_fail.sol | 5 +++ ...hielded_rational_number_exp_limit_fine.sol | 4 ++ .../types/shielded_rational_number_huge.sol | 2 + ...ielded_rational_number_literal_limit_1.sol | 1 + .../shielded_rational_number_too_large.sol | 1 - .../visibility/shielded_array_return.sol | 4 ++ .../visibility/shielded_sbytes_return.sol | 7 +++ .../visibility/shielded_struct_return.sol | 4 ++ 91 files changed, 388 insertions(+), 62 deletions(-) create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_arithmetic_inc_dec_info_leak.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_negative_rational_shift_requires_shielded_result.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_exp_implicit_public_fails.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_exp_requires_shielded_result.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_shift_implicit_public_fails.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_shift_requires_shielded_result.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_fixedbytes_literal.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_fixedbytes_literal_struct.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_array.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_array_nested.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_multiple_args.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_struct.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_struct_positional.sol create mode 100644 test/libsolidity/syntaxTests/shieldedTypes/shielded_dynamic_array_length_warning.sol create mode 100644 test/libsolidity/syntaxTests/types/shielded_constant_expression_leak.sol diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_packed_shielded.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_packed_shielded.sol index 343184bcd..e46699665 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_packed_shielded.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_packed_shielded.sol @@ -5,5 +5,5 @@ contract C { } } // ---- -// Warning 9661: (52-73): Bool Literals converted to shielded bools will leak during contract deployment. +// Warning 9661: (62-73): Bool Literals converted to shielded bools will leak during contract deployment. // TypeError 3648: (100-101): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_types.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_types.sol index a2d5b210f..488201b6a 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_types.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_shielded_types.sol @@ -5,5 +5,5 @@ contract C { } } // ---- -// Warning 9660: (52-76): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (65-76): Literals converted to shielded integers will leak during contract deployment. // TypeError 3648: (97-98): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_selector_shielded.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_selector_shielded.sol index e943ed001..ed37c8e10 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_selector_shielded.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_selector_shielded.sol @@ -5,5 +5,5 @@ contract C { } } // ---- -// Warning 9660: (52-76): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (65-76): Literals converted to shielded integers will leak during contract deployment. // TypeError 3648: (129-130): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_signature_shielded.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_signature_shielded.sol index f960c34a6..8098a4983 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_signature_shielded.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_with_signature_shielded.sol @@ -5,5 +5,5 @@ contract C { } } // ---- -// Warning 9660: (52-76): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (65-76): Literals converted to shielded integers will leak during contract deployment. // TypeError 3648: (126-127): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/array/shielded_array_constructor_length_suint.sol b/test/libsolidity/syntaxTests/array/shielded_array_constructor_length_suint.sol index 02a04f017..879fd8393 100644 --- a/test/libsolidity/syntaxTests/array/shielded_array_constructor_length_suint.sol +++ b/test/libsolidity/syntaxTests/array/shielded_array_constructor_length_suint.sol @@ -5,4 +5,5 @@ contract C { } } // ---- +// Warning 9660: (95-103): Literals converted to shielded integers will leak during contract deployment. // TypeError 9553: (95-103): Invalid type for argument in function call. Invalid implicit conversion from suint256 to uint256 requested. diff --git a/test/libsolidity/syntaxTests/array/shielded_index_addition.sol b/test/libsolidity/syntaxTests/array/shielded_index_addition.sol index b37051caa..601daa240 100644 --- a/test/libsolidity/syntaxTests/array/shielded_index_addition.sol +++ b/test/libsolidity/syntaxTests/array/shielded_index_addition.sol @@ -7,6 +7,7 @@ contract C { } } // ---- -// Warning 9660: (75-109): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (98-109): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (150-161): Literals converted to shielded integers will leak during contract deployment. // TypeError 7407: (136-161): Type suint256 is not implicitly convertible to expected type uint256. -// TypeError 5910: (136-161): Shielded types are not allowed as array indices. \ No newline at end of file +// TypeError 5910: (136-161): Shielded types are not allowed as array indices. diff --git a/test/libsolidity/syntaxTests/array/shielded_index_function_return.sol b/test/libsolidity/syntaxTests/array/shielded_index_function_return.sol index 88bcd2dbf..fe2b7a022 100644 --- a/test/libsolidity/syntaxTests/array/shielded_index_function_return.sol +++ b/test/libsolidity/syntaxTests/array/shielded_index_function_return.sol @@ -10,5 +10,6 @@ contract C { } } // ---- +// Warning 9660: (120-131): Literals converted to shielded integers will leak during contract deployment. // TypeError 7407: (198-216): Type suint256 is not implicitly convertible to expected type uint256. -// TypeError 5910: (198-216): Shielded types are not allowed as array indices. \ No newline at end of file +// TypeError 5910: (198-216): Shielded types are not allowed as array indices. diff --git a/test/libsolidity/syntaxTests/array/shielded_index_multidimensional.sol b/test/libsolidity/syntaxTests/array/shielded_index_multidimensional.sol index 7990c63bd..c2d856772 100644 --- a/test/libsolidity/syntaxTests/array/shielded_index_multidimensional.sol +++ b/test/libsolidity/syntaxTests/array/shielded_index_multidimensional.sol @@ -8,7 +8,7 @@ contract C { } } // ---- -// Warning 9660: (77-104): Literals converted to shielded integers will leak during contract deployment. -// Warning 9660: (114-141): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (93-104): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (130-141): Literals converted to shielded integers will leak during contract deployment. // TypeError 7407: (168-172): Type suint256 is not implicitly convertible to expected type uint256. -// TypeError 5910: (168-172): Shielded types are not allowed as array indices. \ No newline at end of file +// TypeError 5910: (168-172): Shielded types are not allowed as array indices. diff --git a/test/libsolidity/syntaxTests/array/shielded_index_not_allowed.sol b/test/libsolidity/syntaxTests/array/shielded_index_not_allowed.sol index 6c73fa44c..5b36fc7b4 100644 --- a/test/libsolidity/syntaxTests/array/shielded_index_not_allowed.sol +++ b/test/libsolidity/syntaxTests/array/shielded_index_not_allowed.sol @@ -13,6 +13,6 @@ contract C { } } // ---- -// Warning 9660: (96-132): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (121-132): Literals converted to shielded integers will leak during contract deployment. // TypeError 7407: (220-233): Type suint256 is not implicitly convertible to expected type uint256. -// TypeError 5910: (220-233): Shielded types are not allowed as array indices. \ No newline at end of file +// TypeError 5910: (220-233): Shielded types are not allowed as array indices. diff --git a/test/libsolidity/syntaxTests/array/shielded_index_suint16.sol b/test/libsolidity/syntaxTests/array/shielded_index_suint16.sol index 934feeed8..58112268d 100644 --- a/test/libsolidity/syntaxTests/array/shielded_index_suint16.sol +++ b/test/libsolidity/syntaxTests/array/shielded_index_suint16.sol @@ -7,6 +7,6 @@ contract C { } } // ---- -// Warning 9660: (74-98): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (88-98): Literals converted to shielded integers will leak during contract deployment. // TypeError 7407: (124-127): Type suint16 is not implicitly convertible to expected type uint256. -// TypeError 5910: (124-127): Shielded types are not allowed as array indices. \ No newline at end of file +// TypeError 5910: (124-127): Shielded types are not allowed as array indices. diff --git a/test/libsolidity/syntaxTests/array/shielded_index_suint256.sol b/test/libsolidity/syntaxTests/array/shielded_index_suint256.sol index 164fd358b..11f972ff1 100644 --- a/test/libsolidity/syntaxTests/array/shielded_index_suint256.sol +++ b/test/libsolidity/syntaxTests/array/shielded_index_suint256.sol @@ -7,6 +7,6 @@ contract C { } } // ---- -// Warning 9660: (75-101): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (90-101): Literals converted to shielded integers will leak during contract deployment. // TypeError 7407: (128-131): Type suint256 is not implicitly convertible to expected type uint256. -// TypeError 5910: (128-131): Shielded types are not allowed as array indices. \ No newline at end of file +// TypeError 5910: (128-131): Shielded types are not allowed as array indices. diff --git a/test/libsolidity/syntaxTests/array/shielded_index_suint32.sol b/test/libsolidity/syntaxTests/array/shielded_index_suint32.sol index fd2c0fb54..75832a801 100644 --- a/test/libsolidity/syntaxTests/array/shielded_index_suint32.sol +++ b/test/libsolidity/syntaxTests/array/shielded_index_suint32.sol @@ -7,6 +7,6 @@ contract C { } } // ---- -// Warning 9660: (74-98): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (88-98): Literals converted to shielded integers will leak during contract deployment. // TypeError 7407: (124-127): Type suint32 is not implicitly convertible to expected type uint256. -// TypeError 5910: (124-127): Shielded types are not allowed as array indices. \ No newline at end of file +// TypeError 5910: (124-127): Shielded types are not allowed as array indices. diff --git a/test/libsolidity/syntaxTests/array/shielded_index_suint8.sol b/test/libsolidity/syntaxTests/array/shielded_index_suint8.sol index 10b9e068a..d5ecdb16e 100644 --- a/test/libsolidity/syntaxTests/array/shielded_index_suint8.sol +++ b/test/libsolidity/syntaxTests/array/shielded_index_suint8.sol @@ -7,6 +7,6 @@ contract C { } } // ---- -// Warning 9660: (73-95): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (86-95): Literals converted to shielded integers will leak during contract deployment. // TypeError 7407: (120-123): Type suint8 is not implicitly convertible to expected type uint256. -// TypeError 5910: (120-123): Shielded types are not allowed as array indices. \ No newline at end of file +// TypeError 5910: (120-123): Shielded types are not allowed as array indices. diff --git a/test/libsolidity/syntaxTests/array/shielded_push.sol b/test/libsolidity/syntaxTests/array/shielded_push.sol index 1f16ced9e..32d712f83 100644 --- a/test/libsolidity/syntaxTests/array/shielded_push.sol +++ b/test/libsolidity/syntaxTests/array/shielded_push.sol @@ -7,3 +7,5 @@ contract c { data.push(suint(3)); } } +// ---- +// Warning 9660: (148-156): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero.sol index 8c44ecd11..5487928e5 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero.sol @@ -3,3 +3,4 @@ contract C { } // ---- // TypeError 2271: (33-38): Built-in binary operator / cannot be applied to types int_const 1 and int_const 0. +// Warning 9660: (27-39): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_complex.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_complex.sol index ec2bed494..be49309ed 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_complex.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_complex.sol @@ -3,3 +3,4 @@ contract C { } // ---- // TypeError 2271: (33-46): Built-in binary operator / cannot be applied to types int_const 1 and int_const 0. +// Warning 9660: (27-47): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_complex_compound.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_complex_compound.sol index a2f376763..2fb80259e 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_complex_compound.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_complex_compound.sol @@ -3,3 +3,4 @@ contract A { constructor() { a /= suint(((2)*2)%4); } } // ---- +// Warning 9660: (51-67): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_compound.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_compound.sol index ef48d19ce..b618016f4 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_compound.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_compound.sol @@ -3,3 +3,5 @@ contract A { constructor() { a /= suint(0); } } // ---- +// Warning 9660: (27-35): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (62-70): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_nonliteral.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_nonliteral.sol index 92f18fbd2..9d2890f52 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_nonliteral.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_division_by_zero_nonliteral.sol @@ -2,3 +2,4 @@ contract A { constructor() { suint a; a / suint(0); } } // ---- +// Warning 9660: (46-54): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_exponent_fine.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_exponent_fine.sol index 7922c5303..ff933a0b0 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_exponent_fine.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_exponent_fine.sol @@ -6,3 +6,4 @@ contract C { } } // ---- +// Warning 9660: (102-120): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero.sol index e218d00bc..fcbc39763 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero.sol @@ -3,3 +3,4 @@ contract C { } // ---- // TypeError 2271: (34-39): Built-in binary operator % cannot be applied to types int_const 1 and int_const 0. +// Warning 9660: (28-40): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_complex.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_complex.sol index b1dd9ece7..7a3361b97 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_complex.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_complex.sol @@ -3,3 +3,4 @@ contract C { } // ---- // TypeError 2271: (34-50): Built-in binary operator % cannot be applied to types int_const 1 and int_const 0. +// Warning 9660: (28-51): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_complex_compound.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_complex_compound.sol index 58984f748..a9e5f8003 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_complex_compound.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_complex_compound.sol @@ -3,3 +3,5 @@ contract A { constructor() { a %= suint(((2)*2)%4); } } // ---- +// Warning 9660: (27-35): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (62-78): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_compound.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_compound.sol index f40e120ce..83166a26e 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_compound.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_compound.sol @@ -3,3 +3,5 @@ contract A { constructor() { a = suint(5); a %= suint(0); } } // ---- +// Warning 9660: (50-58): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (65-73): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_nonliteral.sol b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_nonliteral.sol index 7dc800381..4c8989c50 100644 --- a/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_nonliteral.sol +++ b/test/libsolidity/syntaxTests/literalOperations/shielded_mod_zero_nonliteral.sol @@ -2,3 +2,4 @@ contract A { constructor() { suint a; a % suint(0); } } // ---- +// Warning 9660: (46-54): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/literals/shielded_ternary_operator_return_type_with_literal_arguments.sol b/test/libsolidity/syntaxTests/literals/shielded_ternary_operator_return_type_with_literal_arguments.sol index 1a48b27a3..2daabef21 100644 --- a/test/libsolidity/syntaxTests/literals/shielded_ternary_operator_return_type_with_literal_arguments.sol +++ b/test/libsolidity/syntaxTests/literals/shielded_ternary_operator_return_type_with_literal_arguments.sol @@ -33,5 +33,9 @@ contract TestTernary } } // ---- -// Warning 9660: (113-138): Literals converted to shielded integers will leak during contract deployment. -// Warning 9660: (148-171): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (127-138): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (161-171): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (591-602): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (617-627): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (876-887): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (906-916): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_010_type_conversion_for_comparison.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_010_type_conversion_for_comparison.sol index 017b79fe9..010d34316 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_010_type_conversion_for_comparison.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_010_type_conversion_for_comparison.sol @@ -2,5 +2,7 @@ contract test { function f() public { suint32(2) == suint64(2); } } // ---- +// Warning 9660: (42-52): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (56-66): Literals converted to shielded integers will leak during contract deployment. // Warning 6133: (42-66): Statement has no effect. // Warning 2018: (20-69): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_011_type_conversion_for_comparison_invalid.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_011_type_conversion_for_comparison_invalid.sol index f57acc3ca..f766233ab 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_011_type_conversion_for_comparison_invalid.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_011_type_conversion_for_comparison_invalid.sol @@ -2,4 +2,6 @@ contract test { function f() public { sint32(2) == suint64(2); } } // ---- +// Warning 9660: (42-51): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (55-65): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (42-65): Built-in binary operator == cannot be applied to types sint32 and suint64. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_113_exp_warn_literal_base_1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_113_exp_warn_literal_base_1.sol index 701225b69..ea7cad3aa 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_113_exp_warn_literal_base_1.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_113_exp_warn_literal_base_1.sol @@ -5,5 +5,5 @@ contract test { } } // ---- -// Warning 9660: (69-91): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (80-91): Literals converted to shielded integers will leak during contract deployment. // Warning 3817: (113-118): Shielded integer exponentiation will leak the exponent value through gas cost. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_114_exp_warn_literal_base_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_114_exp_warn_literal_base_2.sol index 8dd22d804..cdf9fa19d 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_114_exp_warn_literal_base_2.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_114_exp_warn_literal_base_2.sol @@ -5,5 +5,6 @@ contract test { } } // ---- -// Warning 9660: (69-91): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (80-91): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (113-123): Literals converted to shielded integers will leak during contract deployment. // Warning 3817: (113-126): Shielded integer exponentiation will leak the exponent value through gas cost. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_116_shift_warn_literal_base_1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_116_shift_warn_literal_base_1.sol index 57e534962..88386716f 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_116_shift_warn_literal_base_1.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_116_shift_warn_literal_base_1.sol @@ -5,4 +5,4 @@ contract test { } } // ---- -// Warning 9660: (69-91): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (80-91): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_117_shift_warn_literal_base_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_117_shift_warn_literal_base_2.sol index 59e55c34e..3245be3bb 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_117_shift_warn_literal_base_2.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_117_shift_warn_literal_base_2.sol @@ -5,4 +5,5 @@ contract test { } } // ---- -// Warning 9660: (69-91): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (80-91): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (113-123): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_119_shift_warn_literal_base_4.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_119_shift_warn_literal_base_4.sol index c0d166f24..2d91a38af 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_119_shift_warn_literal_base_4.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_119_shift_warn_literal_base_4.sol @@ -5,4 +5,4 @@ contract test { } } // ---- -// Warning 9660: (70-92): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (81-92): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_128_enum_explicit_conversion_is_okay.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_128_enum_explicit_conversion_is_okay.sol index 4a2a83cfc..d1ed3df77 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_128_enum_explicit_conversion_is_okay.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_128_enum_explicit_conversion_is_okay.sol @@ -8,3 +8,5 @@ contract test { suint64 b; } // ---- +// Warning 1457: (108-142): Enums converted to shielded integers will leak during contract deployment. +// Warning 1457: (156-182): Enums converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_129_int_to_enum_explicit_conversion_is_okay.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_129_int_to_enum_explicit_conversion_is_okay.sol index dd523cd90..f7314cbc6 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_129_int_to_enum_explicit_conversion_is_okay.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_129_int_to_enum_explicit_conversion_is_okay.sol @@ -8,3 +8,4 @@ contract test { ActionChoices b; } // ---- +// Warning 9660: (108-116): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_191_negative_integers_to_signed_min.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_191_negative_integers_to_signed_min.sol index 2efd3c8d3..e7ff5736f 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_191_negative_integers_to_signed_min.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_191_negative_integers_to_signed_min.sol @@ -2,3 +2,4 @@ contract test { sint8 i = sint8(-128); } // ---- +// Warning 9660: (30-41): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_193_positive_integers_to_signed_out_of_bound_max.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_193_positive_integers_to_signed_out_of_bound_max.sol index 69d9526b0..0f05dbec0 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_193_positive_integers_to_signed_out_of_bound_max.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_193_positive_integers_to_signed_out_of_bound_max.sol @@ -2,3 +2,4 @@ contract test { sint8 j = sint8(127); } // ---- +// Warning 9660: (30-40): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_196_integer_boolean_or.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_196_integer_boolean_or.sol index f2c9c07be..68c851c8b 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_196_integer_boolean_or.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_196_integer_boolean_or.sol @@ -1,5 +1,5 @@ contract test { fallback() external { suint x = suint(1); suint y = suint(2); x || y; } } // ---- -// Warning 9660: (38-56): Literals converted to shielded integers will leak during contract deployment. -// Warning 9660: (58-76): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (48-56): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (68-76): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (78-84): Built-in binary operator || cannot be applied to types suint256 and suint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_197_integer_boolean_and.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_197_integer_boolean_and.sol index fa01180d5..457e32d86 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_197_integer_boolean_and.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_197_integer_boolean_and.sol @@ -1,5 +1,5 @@ contract test { fallback() external { suint x = suint(1); suint y = suint(2); x && y; } } // ---- -// Warning 9660: (38-56): Literals converted to shielded integers will leak during contract deployment. -// Warning 9660: (58-76): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (48-56): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (68-76): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (78-84): Built-in binary operator && cannot be applied to types suint256 and suint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_198_integer_boolean_not.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_198_integer_boolean_not.sol index 1a8b51bc2..a49b37596 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_198_integer_boolean_not.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_198_integer_boolean_not.sol @@ -1,4 +1,4 @@ contract test { fallback() external { suint x = suint(1); !x; } } // ---- -// Warning 9660: (38-56): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (48-56): Literals converted to shielded integers will leak during contract deployment. // TypeError 4907: (58-60): Built-in unary operator ! cannot be applied to type suint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_199_integer_unsigned_exp_signed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_199_integer_unsigned_exp_signed.sol index 780a7f02b..5676c907b 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_199_integer_unsigned_exp_signed.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_199_integer_unsigned_exp_signed.sol @@ -1,5 +1,6 @@ contract test { fallback() external { suint x = suint(3); sint y = sint(-4); x ** y; } } // ---- -// Warning 9660: (38-56): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (48-56): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (67-75): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (77-83): Built-in binary operator ** cannot be applied to types suint256 and sint256. Exponentiation power is not allowed to be a signed shielded integer type. // Warning 3817: (77-83): Shielded integer exponentiation will leak the exponent value through gas cost. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_200_integer_signed_exp_unsigned.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_200_integer_signed_exp_unsigned.sol index e5efe1fa3..f03b0dd83 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_200_integer_signed_exp_unsigned.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_200_integer_signed_exp_unsigned.sol @@ -4,11 +4,12 @@ contract test { function g() public pure { sint16 x =sint16(3); suint16 y = suint16(4); x ** y; } } // ---- -// Warning 9660: (42-60): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (52-60): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (71-79): Literals converted to shielded integers will leak during contract deployment. // Warning 3817: (81-87): Shielded integer exponentiation will leak the exponent value through gas cost. -// Warning 9660: (122-141): Literals converted to shielded integers will leak during contract deployment. -// Warning 9660: (143-163): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (132-141): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (154-163): Literals converted to shielded integers will leak during contract deployment. // Warning 3817: (165-171): Shielded integer exponentiation will leak the exponent value through gas cost. -// Warning 9660: (206-225): Literals converted to shielded integers will leak during contract deployment. -// Warning 9660: (227-249): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (216-225): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (239-249): Literals converted to shielded integers will leak during contract deployment. // Warning 3817: (251-257): Shielded integer exponentiation will leak the exponent value through gas cost. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_201_integer_signed_exp_signed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_201_integer_signed_exp_signed.sol index 504b15a72..4e3ce97c1 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_201_integer_signed_exp_signed.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_201_integer_signed_exp_signed.sol @@ -15,17 +15,18 @@ contract test { } } // ---- -// Warning 9660: (50-66): Literals converted to shielded integers will leak during contract deployment. -// Warning 9660: (76-92): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (59-66): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (85-92): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (107-113): Built-in binary operator ** cannot be applied to types sint256 and sint256. Exponentiation power is not allowed to be a signed shielded integer type. // Warning 3817: (107-113): Shielded integer exponentiation will leak the exponent value through gas cost. -// Warning 9660: (156-176): Literals converted to shielded integers will leak during contract deployment. -// Warning 9660: (186-206): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (167-176): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (197-206): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (221-227): Built-in binary operator ** cannot be applied to types suint8 and sint16. Exponentiation power is not allowed to be a signed shielded integer type. // Warning 3817: (221-227): Shielded integer exponentiation will leak the exponent value through gas cost. // Warning 3149: (221-227): The result type of the exponentiation operation is equal to the type of the first operand (suint8) ignoring the (larger) type of the second operand (sint16) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other. // TypeError 9640: (216-228): Explicit type conversion not allowed from "suint8" to "sint256". -// Warning 9660: (270-290): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (281-290): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (310-318): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (305-318): Built-in binary operator ** cannot be applied to types sint16 and sint256. Exponentiation power is not allowed to be a signed shielded integer type. // Warning 3817: (305-318): Shielded integer exponentiation will leak the exponent value through gas cost. // Warning 3149: (305-318): The result type of the exponentiation operation is equal to the type of the first operand (sint16) ignoring the (larger) type of the second operand (sint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_arithmetic_inc_dec_info_leak.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_arithmetic_inc_dec_info_leak.sol new file mode 100644 index 000000000..a861c4cd9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_arithmetic_inc_dec_info_leak.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + suint256 a = suint256(1); + a++; + a--; + ++a; + --a; + } +} +// ---- +// Warning 9660: (65-76): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_negative_rational_shift_requires_shielded_result.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_negative_rational_shift_requires_shielded_result.sol new file mode 100644 index 000000000..b35609bde --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_negative_rational_shift_requires_shielded_result.sol @@ -0,0 +1,10 @@ +contract test { + function f() pure internal returns(sint256) { + suint8 x = suint8(2); + sint256 result = -1 << x; // Should work: negative rational << shielded -> sint256 + return result; + } +} +// ---- +// Warning 9660: (85-94): Literals converted to shielded integers will leak during contract deployment. +// TypeError 9574: (104-128): Type int256 is not implicitly convertible to expected type sint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_exp_implicit_public_fails.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_exp_implicit_public_fails.sol new file mode 100644 index 000000000..68ba534e9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_exp_implicit_public_fails.sol @@ -0,0 +1,10 @@ +contract test { + function f() pure internal returns(uint256) { + suint8 x = suint8(2); + uint256 result = 10 ** x; // Should fail: implicit conversion from shielded to public + return result; + } +} +// ---- +// Warning 9660: (85-94): Literals converted to shielded integers will leak during contract deployment. +// Warning 3817: (121-128): Shielded integer exponentiation will leak the exponent value through gas cost. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_exp_requires_shielded_result.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_exp_requires_shielded_result.sol new file mode 100644 index 000000000..bd22fa46c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_exp_requires_shielded_result.sol @@ -0,0 +1,11 @@ +contract test { + function f() pure internal returns(suint256) { + suint8 x = suint8(2); + suint256 result = 10 ** x; // Should work: rational ** shielded -> shielded + return result; + } +} +// ---- +// Warning 9660: (86-95): Literals converted to shielded integers will leak during contract deployment. +// Warning 3817: (123-130): Shielded integer exponentiation will leak the exponent value through gas cost. +// TypeError 9574: (105-130): Type uint256 is not implicitly convertible to expected type suint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_shift_implicit_public_fails.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_shift_implicit_public_fails.sol new file mode 100644 index 000000000..808350884 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_shift_implicit_public_fails.sol @@ -0,0 +1,9 @@ +contract test { + function f() pure internal returns(uint256) { + suint8 x = suint8(100); + uint256 result = 1 << x; // Should fail: implicit conversion from shielded to public + return result; + } +} +// ---- +// Warning 9660: (85-96): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_shift_requires_shielded_result.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_shift_requires_shielded_result.sol new file mode 100644 index 000000000..eadcb2024 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_rational_shift_requires_shielded_result.sol @@ -0,0 +1,10 @@ +contract test { + function f() pure internal returns(suint256) { + suint8 x = suint8(100); + suint256 result = 1 << x; // Should work: rational << shielded -> shielded + return result; + } +} +// ---- +// Warning 9660: (86-97): Literals converted to shielded integers will leak during contract deployment. +// TypeError 9574: (107-131): Type uint256 is not implicitly convertible to expected type suint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_address_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_address_literal.sol index 804f32bd1..759a6d587 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_address_literal.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_address_literal.sol @@ -5,4 +5,4 @@ contract test { } } // ---- -// Warning 9662: (72-137): Address Literals converted to shielded addresses will leak during contract deployment. +// Warning 9662: (85-137): Address Literals converted to shielded addresses will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_bool_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_bool_literal.sol index f7880a78a..79fda603b 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_bool_literal.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_bool_literal.sol @@ -5,4 +5,4 @@ contract test { } } // ---- -// Warning 9661: (69-90): Bool Literals converted to shielded bools will leak during contract deployment. +// Warning 9661: (79-90): Bool Literals converted to shielded bools will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_fixedbytes_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_fixedbytes_literal.sol new file mode 100644 index 000000000..4279d376d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_fixedbytes_literal.sol @@ -0,0 +1,11 @@ +contract C { + constructor() { + sbytes4 x = sbytes4(0x01020304); + sbytes8 y = sbytes8(0x0102030405060708); + } +} +// ---- +// Warning 9663: (53-72): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (94-121): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 2072: (41-50): Unused local variable. +// Warning 2072: (82-91): Unused local variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_fixedbytes_literal_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_fixedbytes_literal_struct.sol new file mode 100644 index 000000000..b20e11e66 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_fixedbytes_literal_struct.sol @@ -0,0 +1,13 @@ +contract C { + struct S { + sbytes4 b; + } + constructor() { + sbytes4 direct = sbytes4(0x01020304); + S memory s = S({b: sbytes4(0x01020304)}); + s.b = direct; + } +} +// ---- +// Warning 9663: (98-117): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (146-165): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_array.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_array.sol new file mode 100644 index 000000000..e1358a375 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_array.sol @@ -0,0 +1,15 @@ +contract C { + constructor() { + suint[2] memory a = [suint(1), suint(2)]; + sbool[2] memory b = [sbool(true), sbool(false)]; + a[0] = suint(3); + b[1] = sbool(true); + } +} +// ---- +// Warning 9660: (62-70): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (72-80): Literals converted to shielded integers will leak during contract deployment. +// Warning 9661: (112-123): Bool Literals converted to shielded bools will leak during contract deployment. +// Warning 9661: (125-137): Bool Literals converted to shielded bools will leak during contract deployment. +// Warning 9660: (155-163): Literals converted to shielded integers will leak during contract deployment. +// Warning 9661: (180-191): Bool Literals converted to shielded bools will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_array_nested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_array_nested.sol new file mode 100644 index 000000000..253368c2c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_array_nested.sol @@ -0,0 +1,16 @@ +contract C { + constructor() { + // Nested arrays + suint[2][2] memory a = [[suint(1), suint(2)], [suint(3), suint(4)]]; + // Array in struct + // Single element array + suint[1] memory b = [suint(5)]; + b[0] = a[0][0]; // Use the variables + } +} +// ---- +// Warning 9660: (91-99): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (101-109): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (113-121): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (123-131): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (223-231): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_multiple_args.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_multiple_args.sol new file mode 100644 index 000000000..95712453e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_multiple_args.sol @@ -0,0 +1,14 @@ +contract C { + struct S { + uint x; + sbool a; + sbool b; + } + constructor() { + S memory s = S(42, sbool(true), sbool(false)); + } +} +// ---- +// Warning 9661: (131-142): Bool Literals converted to shielded bools will leak during contract deployment. +// Warning 9661: (144-156): Bool Literals converted to shielded bools will leak during contract deployment. +// Warning 2072: (112-122): Unused local variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_struct.sol new file mode 100644 index 000000000..70858955c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_struct.sol @@ -0,0 +1,11 @@ +contract C { + struct S { sbool b; } + constructor() { + sbool direct = sbool(true); + S memory s = S({b: sbool(true)}); + s.b = direct; + } +} +// ---- +// Warning 9661: (82-93): Bool Literals converted to shielded bools will leak during contract deployment. +// Warning 9661: (122-133): Bool Literals converted to shielded bools will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_struct_positional.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_struct_positional.sol new file mode 100644 index 000000000..b0ae5647d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/warn_shielded_literal_struct_positional.sol @@ -0,0 +1,11 @@ +contract C { + struct S { sbool b; } + constructor() { + sbool direct = sbool(true); + S memory s = S(sbool(true)); + s.b = direct; + } +} +// ---- +// Warning 9661: (82-93): Bool Literals converted to shielded bools will leak during contract deployment. +// Warning 9661: (118-129): Bool Literals converted to shielded bools will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/parsing/shielded_conditional.sol b/test/libsolidity/syntaxTests/parsing/shielded_conditional.sol index 6611998ed..c95cac937 100644 --- a/test/libsolidity/syntaxTests/parsing/shielded_conditional.sol +++ b/test/libsolidity/syntaxTests/parsing/shielded_conditional.sol @@ -21,7 +21,7 @@ contract TestSbool { } // ---- -// Warning 9661: (294-315): Bool Literals converted to shielded bools will leak during contract deployment. +// Warning 9661: (304-315): Bool Literals converted to shielded bools will leak during contract deployment. // Warning 2018: (25-162): Function state mutability can be restricted to pure // Warning 2018: (168-243): Function state mutability can be restricted to pure // Warning 2018: (249-394): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/shielded_exp_expression.sol b/test/libsolidity/syntaxTests/parsing/shielded_exp_expression.sol index 4f9fa565b..1490ddea2 100644 --- a/test/libsolidity/syntaxTests/parsing/shielded_exp_expression.sol +++ b/test/libsolidity/syntaxTests/parsing/shielded_exp_expression.sol @@ -4,6 +4,7 @@ contract test { } } // ---- +// Warning 9660: (75-83): Literals converted to shielded integers will leak during contract deployment. // Warning 3817: (75-88): Shielded integer exponentiation will leak the exponent value through gas cost. // Warning 2072: (62-72): Unused local variable. // Warning 2018: (20-95): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/shielded_for_loop_simple_initexpr.sol b/test/libsolidity/syntaxTests/parsing/shielded_for_loop_simple_initexpr.sol index f33304449..f8a832112 100644 --- a/test/libsolidity/syntaxTests/parsing/shielded_for_loop_simple_initexpr.sol +++ b/test/libsolidity/syntaxTests/parsing/shielded_for_loop_simple_initexpr.sol @@ -7,7 +7,8 @@ contract test { } } // ---- -// Warning 9660: (62-85): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (74-85): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (107-116): Literals converted to shielded integers will leak during contract deployment. // Warning 5740: (118-121): Unreachable code. // Warning 5740: (160-168): Unreachable code. // Warning 5667: (33-43): Unused function parameter. Remove or comment out the variable name to silence this warning. diff --git a/test/libsolidity/syntaxTests/parsing/shielded_for_loop_single_stmt_body.sol b/test/libsolidity/syntaxTests/parsing/shielded_for_loop_single_stmt_body.sol index 98bf6dc51..b4d22b73c 100644 --- a/test/libsolidity/syntaxTests/parsing/shielded_for_loop_single_stmt_body.sol +++ b/test/libsolidity/syntaxTests/parsing/shielded_for_loop_single_stmt_body.sol @@ -6,6 +6,8 @@ contract test { } } // ---- -// Warning 9660: (62-83): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (75-83): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (102-110): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (116-125): Literals converted to shielded integers will leak during contract deployment. // Warning 5667: (33-43): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning 2018: (20-159): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/shielded_for_loop_vardef_initexpr.sol b/test/libsolidity/syntaxTests/parsing/shielded_for_loop_vardef_initexpr.sol index 558cf1d4b..dbfd2bec5 100644 --- a/test/libsolidity/syntaxTests/parsing/shielded_for_loop_vardef_initexpr.sol +++ b/test/libsolidity/syntaxTests/parsing/shielded_for_loop_vardef_initexpr.sol @@ -6,7 +6,8 @@ contract test { } } // ---- -// Warning 9660: (67-88): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (80-88): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (94-103): Literals converted to shielded integers will leak during contract deployment. // Warning 5740: (105-108): Unreachable code. // Warning 5740: (147-155): Unreachable code. // Warning 5667: (33-43): Unused function parameter. Remove or comment out the variable name to silence this warning. diff --git a/test/libsolidity/syntaxTests/parsing/shielded_if_statement.sol b/test/libsolidity/syntaxTests/parsing/shielded_if_statement.sol index a40cba1f6..29b0573a6 100644 --- a/test/libsolidity/syntaxTests/parsing/shielded_if_statement.sol +++ b/test/libsolidity/syntaxTests/parsing/shielded_if_statement.sol @@ -4,7 +4,8 @@ contract test { } } // ---- -// Warning 9660: (117-135): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (86-94): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (127-135): Literals converted to shielded integers will leak during contract deployment. // Warning 6321: (61-65): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. // Warning 2072: (117-124): Unused local variable. // Warning 2018: (20-144): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/shielded_while_loop.sol b/test/libsolidity/syntaxTests/parsing/shielded_while_loop.sol index 2dd3f5ae0..80559eec0 100644 --- a/test/libsolidity/syntaxTests/parsing/shielded_while_loop.sol +++ b/test/libsolidity/syntaxTests/parsing/shielded_while_loop.sol @@ -5,4 +5,6 @@ contract test { } } // ---- +// Warning 9660: (96-104): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (129-137): Literals converted to shielded integers will leak during contract deployment. // Warning 5740: (113-121): Unreachable code. diff --git a/test/libsolidity/syntaxTests/shieldedTypes/shielded_dynamic_array_length_warning.sol b/test/libsolidity/syntaxTests/shieldedTypes/shielded_dynamic_array_length_warning.sol new file mode 100644 index 000000000..b9cb47767 --- /dev/null +++ b/test/libsolidity/syntaxTests/shieldedTypes/shielded_dynamic_array_length_warning.sol @@ -0,0 +1,9 @@ +contract TestDynamicShieldedArrayWarning { + suint256[] arr; + + function f() public { + arr.push(suint256(1)); + } +} +// ---- +// Warning 9660: (107-118): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_literal_to_payable.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_literal_to_payable.sol index 9c4ce970f..d9f9da16e 100644 --- a/test/libsolidity/syntaxTests/types/address/shielded_address_literal_to_payable.sol +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_literal_to_payable.sol @@ -7,3 +7,5 @@ contract C { } } // ---- +// Warning 9662: (81-133): Address Literals converted to shielded addresses will leak during contract deployment. +// Warning 9662: (173-225): Address Literals converted to shielded addresses will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/address/shielded_address_literal_to_payable_err.sol b/test/libsolidity/syntaxTests/types/address/shielded_address_literal_to_payable_err.sol index 6358e6ff6..d6d965e1f 100644 --- a/test/libsolidity/syntaxTests/types/address/shielded_address_literal_to_payable_err.sol +++ b/test/libsolidity/syntaxTests/types/address/shielded_address_literal_to_payable_err.sol @@ -5,6 +5,6 @@ contract C { } } // ---- +// Warning 9662: (73-125): Address Literals converted to shielded addresses will leak during contract deployment. // TypeError 9574: (52-125): Type saddress is not implicitly convertible to expected type saddress payable. -// Warning 9662: (52-125): Address Literals converted to shielded addresses will leak during contract deployment. // TypeError 9574: (135-198): Type address is not implicitly convertible to expected type saddress payable. diff --git a/test/libsolidity/syntaxTests/types/address/shielded_literal_to_address.sol b/test/libsolidity/syntaxTests/types/address/shielded_literal_to_address.sol index b05aad683..9fcefa43c 100644 --- a/test/libsolidity/syntaxTests/types/address/shielded_literal_to_address.sol +++ b/test/libsolidity/syntaxTests/types/address/shielded_literal_to_address.sol @@ -8,4 +8,6 @@ contract C { } } // ---- -// Warning 9662: (111-176): Address Literals converted to shielded addresses will leak during contract deployment. +// Warning 9662: (124-176): Address Literals converted to shielded addresses will leak during contract deployment. +// Warning 9662: (190-242): Address Literals converted to shielded addresses will leak during contract deployment. +// Warning 9662: (256-317): Address Literals converted to shielded addresses will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/address/shielded_literal_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/shielded_literal_to_payable_address.sol index 66331d1d0..938587eba 100644 --- a/test/libsolidity/syntaxTests/types/address/shielded_literal_to_payable_address.sol +++ b/test/libsolidity/syntaxTests/types/address/shielded_literal_to_payable_address.sol @@ -8,3 +8,5 @@ contract C { } } // ---- +// Warning 9662: (205-257): Address Literals converted to shielded addresses will leak during contract deployment. +// Warning 9662: (280-332): Address Literals converted to shielded addresses will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/address/shielded_payable_conversions_literals.sol b/test/libsolidity/syntaxTests/types/address/shielded_payable_conversions_literals.sol index 7dadb208a..390dba6ce 100644 --- a/test/libsolidity/syntaxTests/types/address/shielded_payable_conversions_literals.sol +++ b/test/libsolidity/syntaxTests/types/address/shielded_payable_conversions_literals.sol @@ -11,3 +11,4 @@ contract C { } } // ---- +// Warning 9662: (181-233): Address Literals converted to shielded addresses will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_arrays.sol b/test/libsolidity/syntaxTests/types/sbytes_arrays.sol index 34e71db68..141f12ece 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_arrays.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_arrays.sol @@ -26,8 +26,11 @@ contract C { } } // ---- +// Warning 9663: (238-251): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (271-298): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (319-395): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // Warning 2072: (435-447): Unused local variable. // Warning 2072: (467-479): Unused local variable. // Warning 2072: (499-513): Unused local variable. // Warning 2072: (566-590): Unused local variable. -// Warning 2072: (619-643): Unused local variable. \ No newline at end of file +// Warning 2072: (619-643): Unused local variable. diff --git a/test/libsolidity/syntaxTests/types/sbytes_basic_declarations.sol b/test/libsolidity/syntaxTests/types/sbytes_basic_declarations.sol index df5b89320..447651d36 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_basic_declarations.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_basic_declarations.sol @@ -17,4 +17,7 @@ contract C { local32 = sbytes32(0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); } } -// ---- \ No newline at end of file +// ---- +// Warning 9663: (287-300): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (319-334): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (354-430): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_explicit_conversions.sol b/test/libsolidity/syntaxTests/types/sbytes_explicit_conversions.sol index 98af74307..b9d5ce30f 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_explicit_conversions.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_explicit_conversions.sol @@ -19,4 +19,8 @@ contract C { sb8 = sbytes8(b8); sb32 = sbytes32(b32); } -} \ No newline at end of file +} +// ---- +// Warning 9663: (72-85): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (109-136): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (162-238): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_function_returns.sol b/test/libsolidity/syntaxTests/types/sbytes_function_returns.sol index 0f4ae44da..0345d8658 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_function_returns.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_function_returns.sol @@ -23,8 +23,11 @@ contract C { } // ---- +// Warning 9663: (91-104): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (195-222): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (315-391): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // Warning 2072: (448-459): Unused local variable. // Warning 2072: (487-498): Unused local variable. // Warning 2072: (526-539): Unused local variable. // Warning 2072: (617-632): Unused local variable. -// Warning 2072: (669-686): Unused local variable. \ No newline at end of file +// Warning 2072: (669-686): Unused local variable. diff --git a/test/libsolidity/syntaxTests/types/sbytes_implicit_conversions_fail.sol b/test/libsolidity/syntaxTests/types/sbytes_implicit_conversions_fail.sol index 4229b11ea..a171769b6 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_implicit_conversions_fail.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_implicit_conversions_fail.sol @@ -20,9 +20,12 @@ contract C { } } // ---- +// Warning 9663: (71-84): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (108-135): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (161-237): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // TypeError 7407: (523-526): Type sbytes1 is not implicitly convertible to expected type bytes1. // TypeError 7407: (541-544): Type sbytes8 is not implicitly convertible to expected type bytes8. // TypeError 7407: (560-564): Type sbytes32 is not implicitly convertible to expected type bytes32. // TypeError 7407: (661-663): Type bytes1 is not implicitly convertible to expected type sbytes1. // TypeError 7407: (679-681): Type bytes8 is not implicitly convertible to expected type sbytes8. -// TypeError 7407: (698-701): Type bytes32 is not implicitly convertible to expected type sbytes32. \ No newline at end of file +// TypeError 7407: (698-701): Type bytes32 is not implicitly convertible to expected type sbytes32. diff --git a/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol b/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol index 0f96ae9f0..f13bd020d 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol @@ -31,3 +31,4 @@ contract C { } } // ---- +// Warning 9663: (880-956): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_operations.sol b/test/libsolidity/syntaxTests/types/sbytes_operations.sol index 45d6f3bec..c32f848b7 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_operations.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_operations.sol @@ -24,6 +24,18 @@ contract C { } } // ---- +// Warning 9663: (71-84): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (108-135): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (161-237): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (315-328): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (362-389): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (426-502): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (677-690): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (718-745): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (771-784): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (812-839): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (866-942): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (971-1047): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // Warning 2072: (291-306): Unused local variable. // Warning 2072: (338-353): Unused local variable. // Warning 2072: (399-416): Unused local variable. @@ -35,4 +47,4 @@ contract C { // Warning 2072: (755-762): Unused local variable. // Warning 2072: (794-802): Unused local variable. // Warning 2072: (849-856): Unused local variable. -// Warning 2072: (952-960): Unused local variable. \ No newline at end of file +// Warning 2072: (952-960): Unused local variable. diff --git a/test/libsolidity/syntaxTests/types/sbytes_public_variables.sol b/test/libsolidity/syntaxTests/types/sbytes_public_variables.sol index 1f465bb1d..ea894d14e 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_public_variables.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_public_variables.sol @@ -14,4 +14,7 @@ contract C { // ---- // TypeError 7091: (88-106): Shielded Types are not supported for public state variables. // TypeError 7091: (112-130): Shielded Types are not supported for public state variables. -// TypeError 7091: (136-156): Shielded Types are not supported for public state variables. \ No newline at end of file +// TypeError 7091: (136-156): Shielded Types are not supported for public state variables. +// Warning 9663: (197-210): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (226-253): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (270-346): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_size_conversions.sol b/test/libsolidity/syntaxTests/types/sbytes_size_conversions.sol index 843cadbc8..76f6e3be4 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_size_conversions.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_size_conversions.sol @@ -16,6 +16,9 @@ contract C { } } // ---- +// Warning 9663: (71-84): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (108-135): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (161-237): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // TypeError 7407: (639-642): Type sbytes8 is not implicitly convertible to expected type sbytes1. // TypeError 7407: (705-709): Type sbytes32 is not implicitly convertible to expected type sbytes1. -// TypeError 7407: (772-776): Type sbytes32 is not implicitly convertible to expected type sbytes8. \ No newline at end of file +// TypeError 7407: (772-776): Type sbytes32 is not implicitly convertible to expected type sbytes8. diff --git a/test/libsolidity/syntaxTests/types/sbytes_struct.sol b/test/libsolidity/syntaxTests/types/sbytes_struct.sol index 1a4824054..b1d05f9ba 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_struct.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_struct.sol @@ -29,6 +29,12 @@ contract C { } } // ---- +// Warning 9663: (261-274): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (296-323): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (350-426): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (562-575): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (595-622): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (647-723): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // Warning 2072: (823-837): Unused local variable. // Warning 2072: (858-874): Unused local variable. -// Warning 2072: (897-913): Unused local variable. \ No newline at end of file +// Warning 2072: (897-913): Unused local variable. diff --git a/test/libsolidity/syntaxTests/types/shielded_constant_expression_leak.sol b/test/libsolidity/syntaxTests/types/shielded_constant_expression_leak.sol new file mode 100644 index 000000000..e195aabb1 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/shielded_constant_expression_leak.sol @@ -0,0 +1,45 @@ +contract C { + function f() public pure { + // BinaryOperation folding to RationalNumber → shielded integer + sint a; + a = sint(0 ** 1E1233); + a = sint(1 ** 1E1233); + a = sint(2 + 3); + a = sint(10 - 7); + a = sint(2 * 3); + + // UnaryOperation folding to RationalNumber → shielded integer + a = sint(-1 ** 1E1233); + a = sint(-(2 + 3)); + + // Parenthesised literal → shielded integer (still a Literal node) + a = sint(42); + + // Direct literal → shielded integer (baseline) + a = sint(0E123456789); + + // BinaryOperation → shielded unsigned integer + suint b; + b = suint(2 + 3); + b = suint(10 * 20); + + // BinaryOperation → shielded fixed bytes + sbytes32 c; + c = sbytes32(2 + 3); + c = sbytes32(0xff * 2); + } +} +// ---- +// Warning 9660: (146-163): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (177-194): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (208-219): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (233-245): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (259-270): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (358-376): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (390-404): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (496-504): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (577-594): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (683-695): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (709-723): Literals converted to shielded integers will leak during contract deployment. +// Warning 9663: (810-825): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (839-857): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/shielded_rational_number_bitshift_limit.sol b/test/libsolidity/syntaxTests/types/shielded_rational_number_bitshift_limit.sol index f21a57b96..e33f46df7 100644 --- a/test/libsolidity/syntaxTests/types/shielded_rational_number_bitshift_limit.sol +++ b/test/libsolidity/syntaxTests/types/shielded_rational_number_bitshift_limit.sol @@ -9,5 +9,6 @@ contract c { // ---- // TypeError 9640: (72-87): Explicit type conversion not allowed from "int_const 5221...(1225 digits omitted)...5168" to "sint256". Literal is too large to fit in sint256. // TypeError 2271: (145-154): Built-in binary operator << cannot be applied to types int_const 1 and int_const 4096. +// Warning 9660: (140-155): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (187-200): Built-in binary operator << cannot be applied to types int_const 1000...(1226 digits omitted)...0000 and int_const 2. // TypeError 9640: (182-201): Explicit type conversion not allowed from "int_const 1000...(1226 digits omitted)...0000" to "sint256". Literal is too large to fit in sint256. diff --git a/test/libsolidity/syntaxTests/types/shielded_rational_number_exp_limit_fail.sol b/test/libsolidity/syntaxTests/types/shielded_rational_number_exp_limit_fail.sol index 0a2fddda8..7bbaab65e 100644 --- a/test/libsolidity/syntaxTests/types/shielded_rational_number_exp_limit_fail.sol +++ b/test/libsolidity/syntaxTests/types/shielded_rational_number_exp_limit_fail.sol @@ -25,10 +25,15 @@ contract c { // TypeError 9640: (133-182): Explicit type conversion not allowed from "int_const 1340...(147 digits omitted)...4096" to "sint256". Literal is too large to fit in sint256. // TypeError 2271: (219-235): Built-in binary operator ** cannot be applied to types int_const 4 and int_const 1340...(147 digits omitted)...4096. // TypeError 2271: (201-237): Built-in binary operator ** cannot be applied to types int_const 4 and int_const -115...(71 digits omitted)...9936. +// Warning 9660: (196-238): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (257-268): Built-in binary operator ** cannot be applied to types int_const 2 and int_const 1000...(1226 digits omitted)...0000. +// Warning 9660: (252-269): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (288-300): Built-in binary operator ** cannot be applied to types int_const -2 and int_const 1000...(1226 digits omitted)...0000. +// Warning 9660: (283-301): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (320-332): Built-in binary operator ** cannot be applied to types int_const 2 and int_const -100...(1227 digits omitted)...0000. +// Warning 9660: (315-333): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (352-365): Built-in binary operator ** cannot be applied to types int_const -2 and int_const -100...(1227 digits omitted)...0000. +// Warning 9660: (347-366): Literals converted to shielded integers will leak during contract deployment. // TypeError 2271: (385-396): Built-in binary operator ** cannot be applied to types int_const 1000...(1226 digits omitted)...0000 and int_const 2. // TypeError 9640: (380-397): Explicit type conversion not allowed from "int_const 1000...(1226 digits omitted)...0000" to "sint256". Literal is too large to fit in sint256. // TypeError 2271: (416-428): Built-in binary operator ** cannot be applied to types int_const -100...(1227 digits omitted)...0000 and int_const 2. diff --git a/test/libsolidity/syntaxTests/types/shielded_rational_number_exp_limit_fine.sol b/test/libsolidity/syntaxTests/types/shielded_rational_number_exp_limit_fine.sol index c5fe0342c..a933d05a7 100644 --- a/test/libsolidity/syntaxTests/types/shielded_rational_number_exp_limit_fine.sol +++ b/test/libsolidity/syntaxTests/types/shielded_rational_number_exp_limit_fine.sol @@ -8,3 +8,7 @@ contract c { } } // ---- +// Warning 9660: (72-89): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (103-120): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (134-152): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (166-183): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/shielded_rational_number_huge.sol b/test/libsolidity/syntaxTests/types/shielded_rational_number_huge.sol index 6661442b1..86d1fe75e 100644 --- a/test/libsolidity/syntaxTests/types/shielded_rational_number_huge.sol +++ b/test/libsolidity/syntaxTests/types/shielded_rational_number_huge.sol @@ -8,3 +8,5 @@ contract C { } } // ---- +// Warning 9660: (112-185): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (306-380): Literals converted to shielded integers will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/shielded_rational_number_literal_limit_1.sol b/test/libsolidity/syntaxTests/types/shielded_rational_number_literal_limit_1.sol index 6fb4027d8..a985ca679 100644 --- a/test/libsolidity/syntaxTests/types/shielded_rational_number_literal_limit_1.sol +++ b/test/libsolidity/syntaxTests/types/shielded_rational_number_literal_limit_1.sol @@ -6,4 +6,5 @@ contract c { } } // ---- +// Warning 9660: (76-98): Literals converted to shielded integers will leak during contract deployment. // TypeError 2826: (136-142): Invalid literal value. diff --git a/test/libsolidity/syntaxTests/types/shielded_rational_number_too_large.sol b/test/libsolidity/syntaxTests/types/shielded_rational_number_too_large.sol index fbd7dd503..b0b840d29 100644 --- a/test/libsolidity/syntaxTests/types/shielded_rational_number_too_large.sol +++ b/test/libsolidity/syntaxTests/types/shielded_rational_number_too_large.sol @@ -8,5 +8,4 @@ contract C { // ---- // TypeError 9574: (52-66): Type int_const 256 is not implicitly convertible to expected type suint8. // TypeError 9640: (87-98): Explicit type conversion not allowed from "int_const 256" to "suint8". Literal is too large to fit in suint8. -// Warning 9660: (76-98): Literals converted to shielded integers will leak during contract deployment. // TypeError 9640: (118-129): Explicit type conversion not allowed from "int_const -129" to "sint8". Literal is too large to fit in sint8. diff --git a/test/libsolidity/syntaxTests/visibility/shielded_array_return.sol b/test/libsolidity/syntaxTests/visibility/shielded_array_return.sol index fd5e78330..546eca765 100644 --- a/test/libsolidity/syntaxTests/visibility/shielded_array_return.sol +++ b/test/libsolidity/syntaxTests/visibility/shielded_array_return.sol @@ -39,6 +39,10 @@ contract C { } // ---- // TypeError 7492: (155-172): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// Warning 9660: (244-256): Literals converted to shielded integers will leak during contract deployment. // TypeError 7492: (341-358): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// Warning 9660: (430-442): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (654-666): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (875-887): Literals converted to shielded integers will leak during contract deployment. // TypeError 7492: (1029-1047): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. // TypeError 7492: (1171-1189): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. diff --git a/test/libsolidity/syntaxTests/visibility/shielded_sbytes_return.sol b/test/libsolidity/syntaxTests/visibility/shielded_sbytes_return.sol index 2b36eabae..545d4f8ff 100644 --- a/test/libsolidity/syntaxTests/visibility/shielded_sbytes_return.sol +++ b/test/libsolidity/syntaxTests/visibility/shielded_sbytes_return.sol @@ -33,7 +33,14 @@ contract C { } // ---- // TypeError 7492: (134-141): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// Warning 9663: (160-173): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // TypeError 7492: (237-245): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// Warning 9663: (264-308): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // TypeError 7492: (372-380): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// Warning 9663: (399-475): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // TypeError 7492: (599-606): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// Warning 9663: (625-638): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. // TypeError 7492: (698-706): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// Warning 9663: (725-801): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (926-1002): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (1124-1200): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/visibility/shielded_struct_return.sol b/test/libsolidity/syntaxTests/visibility/shielded_struct_return.sol index 693984cf8..d96af84dd 100644 --- a/test/libsolidity/syntaxTests/visibility/shielded_struct_return.sol +++ b/test/libsolidity/syntaxTests/visibility/shielded_struct_return.sol @@ -26,4 +26,8 @@ contract C { } // ---- // TypeError 7492: (292-309): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// Warning 9660: (342-353): Literals converted to shielded integers will leak during contract deployment. // TypeError 7492: (424-441): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// Warning 9660: (474-485): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (644-655): Literals converted to shielded integers will leak during contract deployment. +// Warning 9660: (811-822): Literals converted to shielded integers will leak during contract deployment. From 41c92370281063bc2cbba29c83e4e4b4a8ae7bc4 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Thu, 12 Feb 2026 20:47:17 -0500 Subject: [PATCH 65/73] fix(analysis): warn on shielded literal conversions in all contexts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move literal-to-shielded warning check from endVisit(VariableDeclarationStatement) into typeCheckTypeConversionAndRetrieveReturnType so it fires for all explicit type conversions — assignments, function arguments, return statements, and array pushes — not just variable declarations. Extract the warning logic into a reusable checkLiteralToShielded helper that checks annotation().type-based cases (RationalNumber, Enum) before the Literal AST node cast, allowing constant expressions like 0 ** 1E1233 to also trigger warnings. Add checkMsgValueToShielded helper for msg.value-to-shielded warnings. --- libsolidity/analysis/TypeChecker.cpp | 172 ++++++++++++++++++++------- libsolidity/analysis/TypeChecker.h | 15 +++ 2 files changed, 141 insertions(+), 46 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 32c2985e3..edd4ebe51 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1493,52 +1493,9 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) result.message() ); } - if (auto funcCall = dynamic_cast(_statement.initialValue())) - { - auto const& args = funcCall->arguments(); - if (!args.empty()) - { - if (auto literal = dynamic_cast(args.front().get())) - { - if (var.annotation().type->category()==Type::Category::ShieldedBool) - { - std::string val = literal->value(); - if (val == "true" || val == "false") - m_errorReporter.warning( - 9661_error, - _statement.location(), - "Bool Literals converted to shielded bools will leak during contract deployment." - ); - } - else if (literal->looksLikeAddress() && var.annotation().type->category()==Type::Category::ShieldedAddress) - { - if (literal->passesAddressChecksum()) { - m_errorReporter.warning( - 9662_error, - _statement.location(), - "Address Literals converted to shielded addresses will leak during contract deployment." - ); - } - } - else if (args.front()->annotation().type->category()==Type::Category::RationalNumber && var.annotation().type->category()==Type::Category::ShieldedInteger) - { - m_errorReporter.warning( - 9660_error, - _statement.location(), - "Literals converted to shielded integers will leak during contract deployment." - ); - } - else if (args.front()->annotation().type->category()==Type::Category::Enum && var.annotation().type->category()==Type::Category::ShieldedInteger) - { - m_errorReporter.warning( - 1457_error, - _statement.location(), - "Enums converted to shielded integers will leak during contract deployment." - ); - } - } - } - } + // Check for msg.value being assigned to a shielded type + if (_statement.initialValue() && var.annotation().type) + checkMsgValueToShielded(*_statement.initialValue(), *var.annotation().type); } if (valueTypes.size() != variables.size()) @@ -2142,6 +2099,15 @@ Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( ); } } + + // Check for literals being converted to shielded types + if ( + resultType->category() == Type::Category::ShieldedBool || + resultType->category() == Type::Category::ShieldedAddress || + resultType->category() == Type::Category::ShieldedInteger || + resultType->category() == Type::Category::ShieldedFixedBytes + ) + checkLiteralToShielded(*arguments.front(), *resultType, _functionCall.location()); } else { @@ -4409,6 +4375,120 @@ void TypeChecker::checkErrorAndEventParameters(CallableDeclaration const& _calla } } +void TypeChecker::checkLiteralToShielded( + Expression const& _expression, + Type const& _targetType, + langutil::SourceLocation const& _location +) +{ + // Cases that only need annotation().type, not a Literal AST node. + // This covers constant expressions (BinaryOperation, UnaryOperation, etc.) + // that fold to RationalNumber or Enum types. + if ( + _expression.annotation().type && + _expression.annotation().type->category() == Type::Category::RationalNumber && + _targetType.category() == Type::Category::ShieldedInteger + ) + { + m_errorReporter.warning( + 9660_error, + _location, + "Literals converted to shielded integers will leak during contract deployment." + ); + return; + } + else if ( + _expression.annotation().type && + _expression.annotation().type->category() == Type::Category::Enum && + _targetType.category() == Type::Category::ShieldedInteger + ) + { + m_errorReporter.warning( + 1457_error, + _location, + "Enums converted to shielded integers will leak during contract deployment." + ); + return; + } + else if ( + _expression.annotation().type && + _expression.annotation().type->category() == Type::Category::RationalNumber && + _targetType.category() == Type::Category::ShieldedFixedBytes + ) + { + m_errorReporter.warning( + 9663_error, + _location, + "FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment." + ); + return; + } + + // Cases that need the actual Literal AST node for value inspection. + auto literal = dynamic_cast(&_expression); + if (!literal) + return; + + if (_targetType.category() == Type::Category::ShieldedBool) + { + std::string val = literal->value(); + if (val == "true" || val == "false") + m_errorReporter.warning( + 9661_error, + _location, + "Bool Literals converted to shielded bools will leak during contract deployment." + ); + } + else if (literal->looksLikeAddress() && _targetType.category() == Type::Category::ShieldedAddress) + { + if (literal->passesAddressChecksum()) + m_errorReporter.warning( + 9662_error, + _location, + "Address Literals converted to shielded addresses will leak during contract deployment." + ); + } +} + +void TypeChecker::checkMsgValueToShielded( + Expression const& _expression, + Type const& _targetType +) +{ + // Only warn if target type is or contains a shielded type + if (!_targetType.isShielded() && !_targetType.containsShieldedType()) + return; + + // Check if expression is msg.value directly + if (auto memberAccess = dynamic_cast(&_expression)) + { + if (memberAccess->memberName() == "value") + { + if (auto identifier = dynamic_cast(&memberAccess->expression())) + { + if (identifier->name() == "msg") + { + m_errorReporter.warning( + 9664_error, + memberAccess->location(), + "msg.value is always publicly visible on-chain. " + "Assigning it to a shielded type does not hide the transaction value from observers." + ); + return; + } + } + } + } + + // Recurse into type conversion arguments: suint256(msg.value) + if (auto funcCall = dynamic_cast(&_expression)) + { + for (auto const& arg : funcCall->arguments()) + if (arg) + checkMsgValueToShielded(*arg, _targetType); + } +} + Declaration const& TypeChecker::dereference(Identifier const& _identifier) const { solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored."); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 9622c2176..4f71c1aab 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -166,6 +166,21 @@ class TypeChecker: private ASTConstVisitor void checkErrorAndEventParameters(CallableDeclaration const& _callable); + /// Checks if a literal expression is being converted to a shielded type and emits a warning. + /// This is the core check that matches the original warning logic. + void checkLiteralToShielded( + Expression const& _expression, + Type const& _targetType, + langutil::SourceLocation const& _location + ); + + /// Checks if msg.value is being assigned to a shielded type and emits a warning, + /// since msg.value is always publicly visible on-chain. + void checkMsgValueToShielded( + Expression const& _expression, + Type const& _targetType + ); + /// @returns the referenced declaration and throws on error. Declaration const& dereference(Identifier const& _identifier) const; /// @returns the referenced declaration and throws on error. From 4510920a4ea368ae64de98cd45e7951bb161f9bd Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 13 Feb 2026 13:52:02 -0500 Subject: [PATCH 66/73] test(types): update existing tests for sbytes dynamic byte array --- .../shielded_105_constant_input_parameter.sol | 1 + .../syntaxTests/types/sbytes_no_dynamic_byte_array.sol | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_105_constant_input_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_105_constant_input_parameter.sol index b8c739c02..b2c8f39cf 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_105_constant_input_parameter.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shielded_105_constant_input_parameter.sol @@ -3,4 +3,5 @@ contract test { } // ---- // DeclarationError 1788: (31-56): The "constant" keyword can only be used for state variables or variables at file level. +// DeclarationError 7491: (31-56): Shielded objects cannot be set to constant or immutable. // TypeError 9259: (31-56): Only constants of value type and byte array type are implemented. diff --git a/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol b/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol index f13bd020d..6142b5eb8 100644 --- a/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol +++ b/test/libsolidity/syntaxTests/types/sbytes_no_dynamic_byte_array.sol @@ -1,6 +1,6 @@ -// Byte array helpers (resize, push, pop, transitLongToShort) only apply to -// bytes/string. There is no shielded dynamic bytes type. sbytesN is a -// fixed-size value type that does not route through byte array helpers. +// Byte array helpers (resize, push, pop, transitLongToShort) apply to +// bytes/string and sbytes. sbytesN is a fixed-size value type that does +// not route through byte array helpers. contract C { bytes public b; string public s; @@ -31,4 +31,4 @@ contract C { } } // ---- -// Warning 9663: (880-956): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (845-921): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. From 9fd78c5a7984b6e17f90c38691813b8e27891704 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 13 Feb 2026 13:52:08 -0500 Subject: [PATCH 67/73] test(types): add sbytes dynamic shielded byte array tests --- .../cmdlineTests/shielded_sbytes_dynamic/args | 1 + test/cmdlineTests/shielded_sbytes_dynamic/err | 5 + .../shielded_sbytes_dynamic/input.sol | 9 + .../shielded_sbytes_dynamic/output | 232 +++++++++++ .../shielded_sbytes_dynamic_push/args | 1 + .../shielded_sbytes_dynamic_push/err | 11 + .../shielded_sbytes_dynamic_push/input.sol | 12 + .../shielded_sbytes_dynamic_push/output | 394 ++++++++++++++++++ .../types/sbytes_dynamic_assignment.sol | 28 ++ .../types/sbytes_dynamic_calldata_index.sol | 21 + .../types/sbytes_dynamic_calldata_length.sol | 10 + .../sbytes_dynamic_calldata_to_storage.sol | 25 ++ .../types/sbytes_dynamic_comparison.sol | 22 + .../sbytes_dynamic_copy_cleanup_cload.sol | 34 ++ .../types/sbytes_dynamic_copy_empty.sol | 25 ++ .../sbytes_dynamic_copy_memory_to_storage.sol | 23 + .../sbytes_dynamic_copy_removes_data.sol | 32 ++ .../sbytes_dynamic_copy_storage_to_memory.sol | 25 ++ ...sbytes_dynamic_copy_storage_to_storage.sol | 30 ++ .../types/sbytes_dynamic_copy_to_storage.sol | 53 +++ .../types/sbytes_dynamic_delete.sol | 26 ++ .../sbytes_dynamic_delete_cleanup_cload.sol | 31 ++ .../types/sbytes_dynamic_delete_element.sol | 21 + .../types/sbytes_dynamic_delete_entire.sol | 27 ++ ...sbytes_dynamic_dirty_memory_to_storage.sol | 33 ++ .../types/sbytes_dynamic_explicit_cast.sol | 76 ++++ .../types/sbytes_dynamic_index_access.sol | 30 ++ .../sbytes_dynamic_index_storage_write.sol | 29 ++ .../types/sbytes_dynamic_length.sol | 29 ++ .../types/sbytes_dynamic_mapping.sol | 38 ++ .../types/sbytes_dynamic_memory.sol | 14 + .../types/sbytes_dynamic_pop_basic.sol | 18 + .../sbytes_dynamic_pop_cleanup_cload.sol | 31 ++ .../types/sbytes_dynamic_pop_copy_long.sol | 18 + .../sbytes_dynamic_pop_empty_exception.sol | 15 + .../sbytes_dynamic_pop_long_storage_empty.sol | 21 + .../types/sbytes_dynamic_pop_masking_long.sol | 19 + .../sbytes_dynamic_pop_storage_empty.sol | 18 + .../types/sbytes_dynamic_push_basic.sol | 22 + .../types/sbytes_dynamic_push_pop.sol | 30 ++ .../types/sbytes_dynamic_push_transition.sol | 17 + .../types/sbytes_dynamic_short_long.sol | 41 ++ .../types/sbytes_dynamic_storage.sol | 20 + .../sbytes_dynamic_storage_cleanup_cload.sol | 35 ++ .../sbytes_dynamic_struct_copy_and_delete.sol | 53 +++ .../types/sbytes_dynamic_struct_storage.sol | 70 ++++ .../shielded_msg_data_warning.sol | 9 + .../types/sbytes_dynamic_abi_encode.sol | 14 + .../types/sbytes_dynamic_array_of.sol | 10 + .../types/sbytes_dynamic_assignment.sol | 15 + .../types/sbytes_dynamic_constant.sol | 5 + .../types/sbytes_dynamic_conversions.sol | 30 ++ .../types/sbytes_dynamic_declaration.sol | 16 + .../types/sbytes_dynamic_delete.sol | 8 + .../types/sbytes_dynamic_events_errors.sol | 12 + .../types/sbytes_dynamic_function_params.sol | 26 ++ .../types/sbytes_dynamic_function_returns.sol | 26 ++ .../types/sbytes_dynamic_index_access.sol | 19 + .../types/sbytes_dynamic_length.sol | 15 + .../types/sbytes_dynamic_length_warning.sol | 5 + .../types/sbytes_dynamic_mapping_value.sol | 9 + .../syntaxTests/types/sbytes_dynamic_new.sol | 15 + .../types/sbytes_dynamic_pop_syntax.sol | 9 + .../types/sbytes_dynamic_public_variable.sol | 5 + .../sbytes_dynamic_push_assign_multi.sol | 36 ++ .../types/sbytes_dynamic_push_pop.sol | 19 + .../sbytes_dynamic_reference_compare.sol | 5 + .../types/sbytes_dynamic_slice_memory.sol | 9 + .../types/sbytes_dynamic_slice_storage.sol | 10 + .../sbytes_dynamic_storage_locations.sol | 19 + .../types/sbytes_dynamic_struct.sol | 15 + .../types/sbytes_dynamic_to_fixed.sol | 16 + 72 files changed, 2152 insertions(+) create mode 100644 test/cmdlineTests/shielded_sbytes_dynamic/args create mode 100644 test/cmdlineTests/shielded_sbytes_dynamic/err create mode 100644 test/cmdlineTests/shielded_sbytes_dynamic/input.sol create mode 100644 test/cmdlineTests/shielded_sbytes_dynamic/output create mode 100644 test/cmdlineTests/shielded_sbytes_dynamic_push/args create mode 100644 test/cmdlineTests/shielded_sbytes_dynamic_push/err create mode 100644 test/cmdlineTests/shielded_sbytes_dynamic_push/input.sol create mode 100644 test/cmdlineTests/shielded_sbytes_dynamic_push/output create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_assignment.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_index.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_length.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_to_storage.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_comparison.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_copy_cleanup_cload.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_copy_empty.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_copy_memory_to_storage.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_copy_removes_data.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_copy_storage_to_memory.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_copy_storage_to_storage.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_copy_to_storage.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_delete.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_delete_cleanup_cload.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_delete_element.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_delete_entire.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_dirty_memory_to_storage.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_explicit_cast.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_index_access.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_index_storage_write.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_length.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_mapping.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_memory.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_pop_basic.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_pop_cleanup_cload.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_pop_copy_long.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_pop_empty_exception.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_pop_long_storage_empty.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_pop_masking_long.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_pop_storage_empty.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_push_basic.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_push_pop.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_push_transition.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_short_long.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_storage.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_storage_cleanup_cload.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_struct_copy_and_delete.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_struct_storage.sol create mode 100644 test/libsolidity/syntaxTests/shieldedTypes/shielded_msg_data_warning.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_abi_encode.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_array_of.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_assignment.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_constant.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_conversions.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_declaration.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_delete.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_events_errors.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_function_params.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_function_returns.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_index_access.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_length.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_length_warning.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_mapping_value.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_new.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_pop_syntax.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_public_variable.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_push_assign_multi.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_push_pop.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_reference_compare.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_slice_memory.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_slice_storage.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_storage_locations.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_struct.sol create mode 100644 test/libsolidity/syntaxTests/types/sbytes_dynamic_to_fixed.sol diff --git a/test/cmdlineTests/shielded_sbytes_dynamic/args b/test/cmdlineTests/shielded_sbytes_dynamic/args new file mode 100644 index 000000000..63727f0d9 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes_dynamic/args @@ -0,0 +1 @@ +--asm diff --git a/test/cmdlineTests/shielded_sbytes_dynamic/err b/test/cmdlineTests/shielded_sbytes_dynamic/err new file mode 100644 index 000000000..6a1f5d816 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes_dynamic/err @@ -0,0 +1,5 @@ +Warning: Dynamic arrays with shielded element types store their length confidentially, but an upper bound on the length may still be observable through gas cost analysis. + --> input.sol:5:5: + | +5 | sbytes data; + | ^^^^^^^^^^^ diff --git a/test/cmdlineTests/shielded_sbytes_dynamic/input.sol b/test/cmdlineTests/shielded_sbytes_dynamic/input.sol new file mode 100644 index 000000000..d1f8defba --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes_dynamic/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract Blah { + sbytes data; + function test() external { + data.push(sbytes1(0x42)); + } +} diff --git a/test/cmdlineTests/shielded_sbytes_dynamic/output b/test/cmdlineTests/shielded_sbytes_dynamic/output new file mode 100644 index 000000000..46e779e4e --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes_dynamic/output @@ -0,0 +1,232 @@ + +======= input.sol:Blah ======= +EVM assembly: + /* "input.sol":60:165 contract Blah {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "input.sol":60:165 contract Blah {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0xf8a8fd6d + eq + tag_3 + jumpi + tag_2: + revert(0x00, 0x00) + /* "input.sol":97:163 function test() external {... */ + tag_3: + tag_4 + tag_5 + jump // in + tag_4: + stop + tag_5: + /* "input.sol":132:136 data */ + 0x00 + /* "input.sol":150:154 0x42 */ + 0x42 + /* "input.sol":142:155 sbytes1(0x42) */ + 0xf8 + shl + /* "input.sol":132:156 data.push(sbytes1(0x42)) */ + swap1 + dup1 + dup1 + cload + dup1 + tag_7 + swap1 + tag_8 + jump // in + tag_7: + dup1 + 0x1f + dup2 + sub + tag_9 + jumpi + dup4 + 0x00 + mstore + keccak256(0x00, 0x20) + not(0xff) + dup5 + and + dup2 + cstore + 0x3f + swap4 + pop + pop + tag_9: + pop + 0x02 + dup3 + add + dup4 + cstore + 0x01 + dup2 + add + swap3 + pop + pop + pop + 0x01 + swap1 + sub + dup2 + cload + 0x01 + and + iszero + tag_11 + jumpi + swap1 + 0x00 + mstore + keccak256(0x00, 0x20) + add + 0x00 + tag_11: + swap1 + swap2 + swap1 + swap2 + swap1 + swap2 + 0x1f + sub + 0x0100 + exp + dup2 + cload + dup2 + 0xff + mul + not + and + swap1 + 0x0100000000000000000000000000000000000000000000000000000000000000 + dup5 + div + mul + or + swap1 + cstore + pop + /* "input.sol":97:163 function test() external {... */ + jump // out + /* "#utility.yul":7:187 */ + tag_12: + /* "#utility.yul":55:132 */ + 0x4e487b7100000000000000000000000000000000000000000000000000000000 + /* "#utility.yul":52:53 */ + 0x00 + /* "#utility.yul":45:133 */ + mstore + /* "#utility.yul":152:156 */ + 0x22 + /* "#utility.yul":149:150 */ + 0x04 + /* "#utility.yul":142:157 */ + mstore + /* "#utility.yul":176:180 */ + 0x24 + /* "#utility.yul":173:174 */ + 0x00 + /* "#utility.yul":166:181 */ + revert + /* "#utility.yul":193:513 */ + tag_8: + /* "#utility.yul":237:243 */ + 0x00 + /* "#utility.yul":274:275 */ + 0x02 + /* "#utility.yul":268:272 */ + dup3 + /* "#utility.yul":264:276 */ + div + /* "#utility.yul":254:276 */ + swap1 + pop + /* "#utility.yul":321:322 */ + 0x01 + /* "#utility.yul":315:319 */ + dup3 + /* "#utility.yul":311:323 */ + and + /* "#utility.yul":342:360 */ + dup1 + /* "#utility.yul":332:413 */ + tag_16 + jumpi + /* "#utility.yul":398:402 */ + 0x7f + /* "#utility.yul":390:396 */ + dup3 + /* "#utility.yul":386:403 */ + and + /* "#utility.yul":376:403 */ + swap2 + pop + /* "#utility.yul":332:413 */ + tag_16: + /* "#utility.yul":460:462 */ + 0x20 + /* "#utility.yul":452:458 */ + dup3 + /* "#utility.yul":449:463 */ + lt + /* "#utility.yul":429:447 */ + dup2 + /* "#utility.yul":426:464 */ + sub + /* "#utility.yul":423:507 */ + tag_17 + jumpi + /* "#utility.yul":479:497 */ + tag_18 + tag_12 + jump // in + tag_18: + /* "#utility.yul":423:507 */ + tag_17: + /* "#utility.yul":244:513 */ + pop + /* "#utility.yul":193:513 */ + swap2 + swap1 + pop + jump // out + + auxdata: +} + diff --git a/test/cmdlineTests/shielded_sbytes_dynamic_push/args b/test/cmdlineTests/shielded_sbytes_dynamic_push/args new file mode 100644 index 000000000..63727f0d9 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes_dynamic_push/args @@ -0,0 +1 @@ +--asm diff --git a/test/cmdlineTests/shielded_sbytes_dynamic_push/err b/test/cmdlineTests/shielded_sbytes_dynamic_push/err new file mode 100644 index 000000000..8fa5c3591 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes_dynamic_push/err @@ -0,0 +1,11 @@ +Warning: Dynamic arrays with shielded element types store their length confidentially, but an upper bound on the length may still be observable through gas cost analysis. + --> input.sol:5:5: + | +5 | sbytes data; + | ^^^^^^^^^^^ + +Warning: Unused local variable. + --> input.sol:10:9: + | +10 | sbytes1 val = data[0]; + | ^^^^^^^^^^^ diff --git a/test/cmdlineTests/shielded_sbytes_dynamic_push/input.sol b/test/cmdlineTests/shielded_sbytes_dynamic_push/input.sol new file mode 100644 index 000000000..9b8bc0947 --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes_dynamic_push/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract Blah { + sbytes data; + + function pushAndRead() external { + data.push(sbytes1(0xAA)); + data.push(sbytes1(0xBB)); + sbytes1 val = data[0]; + } +} diff --git a/test/cmdlineTests/shielded_sbytes_dynamic_push/output b/test/cmdlineTests/shielded_sbytes_dynamic_push/output new file mode 100644 index 000000000..0799f7a3a --- /dev/null +++ b/test/cmdlineTests/shielded_sbytes_dynamic_push/output @@ -0,0 +1,394 @@ + +======= input.sol:Blah ======= +EVM assembly: + /* "input.sol":60:238 contract Blah {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) +tag_1: + pop + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "input.sol":60:238 contract Blah {... */ + mstore(0x40, 0x80) + callvalue + dup1 + iszero + tag_1 + jumpi + revert(0x00, 0x00) + tag_1: + pop + jumpi(tag_2, lt(calldatasize, 0x04)) + shr(0xe0, calldataload(0x00)) + dup1 + 0xb40f7c6c + eq + tag_3 + jumpi + tag_2: + revert(0x00, 0x00) + /* "input.sol":98:236 function pushAndRead() external {... */ + tag_3: + tag_4 + tag_5 + jump // in + tag_4: + stop + tag_5: + /* "input.sol":140:144 data */ + 0x00 + /* "input.sol":158:162 0xAA */ + 0xaa + /* "input.sol":150:163 sbytes1(0xAA) */ + 0xf8 + shl + /* "input.sol":140:164 data.push(sbytes1(0xAA)) */ + swap1 + dup1 + dup1 + cload + dup1 + tag_7 + swap1 + tag_8 + jump // in + tag_7: + dup1 + 0x1f + dup2 + sub + tag_9 + jumpi + dup4 + 0x00 + mstore + keccak256(0x00, 0x20) + not(0xff) + dup5 + and + dup2 + cstore + 0x3f + swap4 + pop + pop + tag_9: + pop + 0x02 + dup3 + add + dup4 + cstore + 0x01 + dup2 + add + swap3 + pop + pop + pop + 0x01 + swap1 + sub + dup2 + cload + 0x01 + and + iszero + tag_11 + jumpi + swap1 + 0x00 + mstore + keccak256(0x00, 0x20) + add + 0x00 + tag_11: + swap1 + swap2 + swap1 + swap2 + swap1 + swap2 + 0x1f + sub + 0x0100 + exp + dup2 + cload + dup2 + 0xff + mul + not + and + swap1 + 0x0100000000000000000000000000000000000000000000000000000000000000 + dup5 + div + mul + or + swap1 + cstore + pop + /* "input.sol":174:178 data */ + 0x00 + /* "input.sol":192:196 0xBB */ + 0xbb + /* "input.sol":184:197 sbytes1(0xBB) */ + 0xf8 + shl + /* "input.sol":174:198 data.push(sbytes1(0xBB)) */ + swap1 + dup1 + dup1 + cload + dup1 + tag_12 + swap1 + tag_8 + jump // in + tag_12: + dup1 + 0x1f + dup2 + sub + tag_13 + jumpi + dup4 + 0x00 + mstore + keccak256(0x00, 0x20) + not(0xff) + dup5 + and + dup2 + cstore + 0x3f + swap4 + pop + pop + tag_13: + pop + 0x02 + dup3 + add + dup4 + cstore + 0x01 + dup2 + add + swap3 + pop + pop + pop + 0x01 + swap1 + sub + dup2 + cload + 0x01 + and + iszero + tag_15 + jumpi + swap1 + 0x00 + mstore + keccak256(0x00, 0x20) + add + 0x00 + tag_15: + swap1 + swap2 + swap1 + swap2 + swap1 + swap2 + 0x1f + sub + 0x0100 + exp + dup2 + cload + dup2 + 0xff + mul + not + and + swap1 + 0x0100000000000000000000000000000000000000000000000000000000000000 + dup5 + div + mul + or + swap1 + cstore + pop + /* "input.sol":208:219 sbytes1 val */ + 0x00 + /* "input.sol":222:226 data */ + 0x00 + /* "input.sol":227:228 0 */ + 0x00 + /* "input.sol":222:229 data[0] */ + dup2 + cload + tag_16 + swap1 + tag_8 + jump // in + tag_16: + dup2 + lt + tag_17 + jumpi + tag_18 + tag_19 + jump // in + tag_18: + tag_17: + dup2 + cload + 0x01 + and + iszero + tag_20 + jumpi + swap1 + 0x00 + mstore + keccak256(0x00, 0x20) + add + 0x00 + tag_20: + swap1 + cload + swap1 + byte + 0x0100000000000000000000000000000000000000000000000000000000000000 + mul + /* "input.sol":208:229 sbytes1 val = data[0] */ + swap1 + pop + /* "input.sol":130:236 {... */ + pop + /* "input.sol":98:236 function pushAndRead() external {... */ + jump // out + /* "#utility.yul":7:187 */ + tag_21: + /* "#utility.yul":55:132 */ + 0x4e487b7100000000000000000000000000000000000000000000000000000000 + /* "#utility.yul":52:53 */ + 0x00 + /* "#utility.yul":45:133 */ + mstore + /* "#utility.yul":152:156 */ + 0x22 + /* "#utility.yul":149:150 */ + 0x04 + /* "#utility.yul":142:157 */ + mstore + /* "#utility.yul":176:180 */ + 0x24 + /* "#utility.yul":173:174 */ + 0x00 + /* "#utility.yul":166:181 */ + revert + /* "#utility.yul":193:513 */ + tag_8: + /* "#utility.yul":237:243 */ + 0x00 + /* "#utility.yul":274:275 */ + 0x02 + /* "#utility.yul":268:272 */ + dup3 + /* "#utility.yul":264:276 */ + div + /* "#utility.yul":254:276 */ + swap1 + pop + /* "#utility.yul":321:322 */ + 0x01 + /* "#utility.yul":315:319 */ + dup3 + /* "#utility.yul":311:323 */ + and + /* "#utility.yul":342:360 */ + dup1 + /* "#utility.yul":332:413 */ + tag_25 + jumpi + /* "#utility.yul":398:402 */ + 0x7f + /* "#utility.yul":390:396 */ + dup3 + /* "#utility.yul":386:403 */ + and + /* "#utility.yul":376:403 */ + swap2 + pop + /* "#utility.yul":332:413 */ + tag_25: + /* "#utility.yul":460:462 */ + 0x20 + /* "#utility.yul":452:458 */ + dup3 + /* "#utility.yul":449:463 */ + lt + /* "#utility.yul":429:447 */ + dup2 + /* "#utility.yul":426:464 */ + sub + /* "#utility.yul":423:507 */ + tag_26 + jumpi + /* "#utility.yul":479:497 */ + tag_27 + tag_21 + jump // in + tag_27: + /* "#utility.yul":423:507 */ + tag_26: + /* "#utility.yul":244:513 */ + pop + /* "#utility.yul":193:513 */ + swap2 + swap1 + pop + jump // out + /* "#utility.yul":519:699 */ + tag_19: + /* "#utility.yul":567:644 */ + 0x4e487b7100000000000000000000000000000000000000000000000000000000 + /* "#utility.yul":564:565 */ + 0x00 + /* "#utility.yul":557:645 */ + mstore + /* "#utility.yul":664:668 */ + 0x32 + /* "#utility.yul":661:662 */ + 0x04 + /* "#utility.yul":654:669 */ + mstore + /* "#utility.yul":688:692 */ + 0x24 + /* "#utility.yul":685:686 */ + 0x00 + /* "#utility.yul":678:693 */ + revert + + auxdata: +} + diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_assignment.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_assignment.sol new file mode 100644 index 000000000..643c208dd --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_assignment.sol @@ -0,0 +1,28 @@ +contract C { + sbytes a; + sbytes b; + + function test() public returns (bool) { + // Fill a + a.push(sbytes1(0x11)); + a.push(sbytes1(0x22)); + a.push(sbytes1(0x33)); + + // Copy a to b + b = a; + + // Verify b has same content + require(uint256(suint256(b.length)) == 3); + require(b[0] == sbytes1(0x11)); + require(b[1] == sbytes1(0x22)); + require(b[2] == sbytes1(0x33)); + + // Modify a, verify b is independent + a[0] = sbytes1(0xFF); + require(b[0] == sbytes1(0x11)); + + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_index.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_index.sol new file mode 100644 index 000000000..616565eb1 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_index.sol @@ -0,0 +1,21 @@ +// Tests index access on sbytes calldata without storing +contract C { + function getFirst(sbytes calldata data) external returns (bool) { + require(data[0] == sbytes1(0x41)); + return true; + } + + function getLast(sbytes calldata data) external returns (bool) { + require(data[uint256(suint256(data.length)) - 1] == sbytes1(0x43)); + return true; + } + + function getAt(sbytes calldata data, uint256 i) external returns (bool) { + require(data[i] == sbytes1(0x42)); + return true; + } +} +// ---- +// getFirst(sbytes): 0x20, 3, 0x4142430000000000000000000000000000000000000000000000000000000000 -> true +// getLast(sbytes): 0x20, 3, 0x4142430000000000000000000000000000000000000000000000000000000000 -> true +// getAt(sbytes,uint256): 0x40, 1, 3, 0x4142430000000000000000000000000000000000000000000000000000000000 -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_length.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_length.sol new file mode 100644 index 000000000..470dfb07f --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_length.sol @@ -0,0 +1,10 @@ +// Tests getting length of sbytes calldata +contract C { + function getLength(sbytes calldata data) external returns (uint256) { + return uint256(suint256(data.length)); + } +} +// ---- +// getLength(sbytes): 0x20, 0 -> 0 +// getLength(sbytes): 0x20, 5, 0x0102030405000000000000000000000000000000000000000000000000000000 -> 5 +// getLength(sbytes): 0x20, 40, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20, 0x2122232425262728000000000000000000000000000000000000000000000000 -> 40 diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_to_storage.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_to_storage.sol new file mode 100644 index 000000000..82d718d95 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_calldata_to_storage.sol @@ -0,0 +1,25 @@ +// Tests copying sbytes calldata to storage for short and long values +// Adapted from calldata_bytes_to_storage.sol +contract C { + sbytes s; + + function setShort(sbytes calldata data) external returns (bool) { + s = data; + require(uint256(suint256(s.length)) == 3); + require(s[0] == sbytes1(0x61)); + require(s[1] == sbytes1(0x62)); + require(s[2] == sbytes1(0x63)); + return true; + } + + function setLong(sbytes calldata data) external returns (bool) { + s = data; + require(uint256(suint256(s.length)) == 40); + require(s[0] == sbytes1(0x01)); + require(s[39] == sbytes1(0x28)); + return true; + } +} +// ---- +// setShort(sbytes): 0x20, 3, 0x6162630000000000000000000000000000000000000000000000000000000000 -> true +// setLong(sbytes): 0x20, 40, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20, 0x2122232425262728000000000000000000000000000000000000000000000000 -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_comparison.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_comparison.sol new file mode 100644 index 000000000..241eaae30 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_comparison.sol @@ -0,0 +1,22 @@ +// Tests sbytes1 == and != comparisons producing sbool +contract C { + sbytes data; + + function test() public returns (bool) { + data.push(sbytes1(0x42)); + data.push(sbytes1(0x43)); + data.push(sbytes1(0x42)); + + // Equal comparison + require(data[0] == sbytes1(0x42)); + require(data[0] == data[2]); + + // Not-equal comparison + require(data[0] != sbytes1(0x43)); + require(data[0] != data[1]); + + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_cleanup_cload.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_cleanup_cload.sol new file mode 100644 index 000000000..df13baaa6 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_cleanup_cload.sol @@ -0,0 +1,34 @@ +// Tests that copying short sbytes over long sbytes clears old data slots +// Adapted from shielded_array_copy_clear_storage.sol +contract C { + sbytes data; + + function test() public returns (bool) { + // Write 70 bytes (long format: 3 data slots) + data = new sbytes(70); + for (uint256 i = 0; i < 70; i++) + data[i] = sbytes1(uint8(i + 1)); + + // Copy short array (1 element) over it + sbytes memory short_arr = new sbytes(1); + short_arr[0] = sbytes1(0x42); + data = short_arr; + + // Verify length is correct + require(uint256(suint256(data.length)) == 1); + require(data[0] == sbytes1(0x42)); + + // Verify old data area slots are zeroed via cload + assembly { + mstore(0, data.slot) + let dataArea := keccak256(0, 0x20) + // All old data slots should be zeroed since we transitioned to short format + if iszero(eq(cload(dataArea), 0)) { revert(0, 0) } + if iszero(eq(cload(add(dataArea, 1)), 0)) { revert(0, 0) } + if iszero(eq(cload(add(dataArea, 2)), 0)) { revert(0, 0) } + } + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_empty.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_empty.sol new file mode 100644 index 000000000..294277c35 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_empty.sol @@ -0,0 +1,25 @@ +// Tests that copying empty sbytes and pushing produces zeroed element +// Adapted from empty_bytes_copy.sol (calldata and assembly removed) +contract C { + sbytes data; + sbytes otherData; + + function fromMemory() public returns (bool) { + sbytes memory t = new sbytes(0); + data = t; + data.push(); + require(data[0] == sbytes1(0x00)); + return true; + } + + function fromStorage() public returns (bool) { + // otherData is empty by default + data = otherData; + data.push(); + require(data[0] == sbytes1(0x00)); + return true; + } +} +// ---- +// fromMemory() -> true +// fromStorage() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_memory_to_storage.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_memory_to_storage.sol new file mode 100644 index 000000000..56d828541 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_memory_to_storage.sol @@ -0,0 +1,23 @@ +// Tests allocating sbytes storage at various lengths and writing via index +// (Memory sbytes index writes are not yet supported, so we use new + storage index writes) +contract C { + sbytes s; + + function test(uint256 len) public returns (bool) { + s = new sbytes(len); + require(uint256(suint256(s.length)) == len); + for (uint256 i = 0; i < len; i++) + s[i] = sbytes1(uint8(i)); + for (uint256 i = 0; i < len; i++) + require(s[i] == sbytes1(uint8(i))); + return true; + } +} +// ---- +// test(uint256): 0 -> true +// test(uint256): 12 -> true +// test(uint256): 31 -> true +// test(uint256): 32 -> true +// test(uint256): 33 -> true +// test(uint256): 63 -> true +// test(uint256): 129 -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_removes_data.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_removes_data.sol new file mode 100644 index 000000000..4a3f19747 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_removes_data.sol @@ -0,0 +1,32 @@ +// Tests that copying empty sbytes over non-empty clears the data +// Adapted from copy_removes_bytes_data.sol (msg.data and storageEmpty removed) +contract C { + sbytes data1; + sbytes data2; + + function set() public returns (bool) { + for (uint256 i = 0; i < 100; i++) + data1.push(sbytes1(uint8(i))); + return true; + } + + function reset() public returns (bool) { + data1 = data2; + return true; + } + + function verify() public returns (bool) { + require(uint256(suint256(data1.length)) == 0); + return true; + } + + function verifyNonEmpty() public returns (bool) { + require(uint256(suint256(data1.length)) > 0); + return true; + } +} +// ---- +// set() -> true +// verifyNonEmpty() -> true +// reset() -> true +// verify() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_storage_to_memory.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_storage_to_memory.sol new file mode 100644 index 000000000..12ed43efa --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_storage_to_memory.sol @@ -0,0 +1,25 @@ +// Tests copying sbytes from storage to memory at various lengths +contract C { + sbytes s; + + function test(uint256 len) public returns (bool) { + // Build storage array via allocation + index writes + s = new sbytes(len); + for (uint256 i = 0; i < len; i++) + s[i] = sbytes1(uint8(i)); + // Copy storage to memory + sbytes memory data = s; + require(uint256(suint256(data.length)) == len); + for (uint256 i = 0; i < len; i++) + require(data[i] == sbytes1(uint8(i))); + return true; + } +} +// ---- +// test(uint256): 0 -> true +// test(uint256): 12 -> true +// test(uint256): 31 -> true +// test(uint256): 32 -> true +// test(uint256): 33 -> true +// test(uint256): 63 -> true +// test(uint256): 129 -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_storage_to_storage.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_storage_to_storage.sol new file mode 100644 index 000000000..5b3c376d6 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_storage_to_storage.sol @@ -0,0 +1,30 @@ +// Tests copying sbytes from one storage variable to another at various lengths +contract C { + sbytes a; + sbytes b; + + function test(uint256 len) public returns (bool) { + // Build 'a' via storage allocation + index writes + a = new sbytes(len); + for (uint256 i = 0; i < len; i++) + a[i] = sbytes1(uint8(i)); + // Verify 'a' is correctly set + for (uint256 i = 0; i < len; i++) + require(a[i] == sbytes1(uint8(i))); + // Copy a -> b + b = a; + // Verify 'b' has same content + require(uint256(suint256(b.length)) == len); + for (uint256 i = 0; i < len; i++) + require(b[i] == sbytes1(uint8(i))); + return true; + } +} +// ---- +// test(uint256): 0 -> true +// test(uint256): 12 -> true +// test(uint256): 31 -> true +// test(uint256): 32 -> true +// test(uint256): 33 -> true +// test(uint256): 63 -> true +// test(uint256): 129 -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_to_storage.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_to_storage.sol new file mode 100644 index 000000000..9bb252a7f --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_copy_to_storage.sol @@ -0,0 +1,53 @@ +// Tests direct storage copy: short, long, and transitions between them +// Adapted from copy_byte_array_to_storage.sol (assembly storage checks removed) +contract C { + sbytes data; + + function test() public returns (bool) { + // Start empty + require(uint256(suint256(data.length)) == 0); + + // Set short value (3 bytes) + data = new sbytes(3); + data[0] = sbytes1(0x61); + data[1] = sbytes1(0x62); + data[2] = sbytes1(0x63); + require(uint256(suint256(data.length)) == 3); + require(data[0] == sbytes1(0x61)); + require(data[1] == sbytes1(0x62)); + require(data[2] == sbytes1(0x63)); + + // Overwrite with long value (70 bytes) + data = new sbytes(70); + for (uint256 i = 0; i < 70; i++) + data[i] = sbytes1(uint8(i + 0x31)); + require(uint256(suint256(data.length)) == 70); + require(data[0] == sbytes1(0x31)); + require(data[69] == sbytes1(uint8(69 + 0x31))); + + // Overwrite long with short (verifies storage cleanup) + data = new sbytes(3); + data[0] = sbytes1(0x61); + data[1] = sbytes1(0x62); + data[2] = sbytes1(0x63); + require(uint256(suint256(data.length)) == 3); + require(data[0] == sbytes1(0x61)); + + // Overwrite short with long again + data = new sbytes(70); + for (uint256 i = 0; i < 70; i++) + data[i] = sbytes1(uint8(i + 0x31)); + require(uint256(suint256(data.length)) == 70); + + // Overwrite long with shorter long (36 bytes) + data = new sbytes(36); + for (uint256 i = 0; i < 36; i++) + data[i] = sbytes1(uint8(i + 0x31)); + require(uint256(suint256(data.length)) == 36); + require(data[35] == sbytes1(uint8(35 + 0x31))); + + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_delete.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_delete.sol new file mode 100644 index 000000000..83826839a --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_delete.sol @@ -0,0 +1,26 @@ +contract C { + sbytes data; + + function getLen() internal view returns (uint256) { + return uint256(suint256(data.length)); + } + + function test() public returns (bool) { + data.push(sbytes1(0xAA)); + data.push(sbytes1(0xBB)); + data.push(sbytes1(0xCC)); + require(getLen() == 3); + + delete data; + require(getLen() == 0); + + // Can push again after delete + data.push(sbytes1(0xDD)); + require(getLen() == 1); + require(data[0] == sbytes1(0xDD)); + + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_delete_cleanup_cload.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_delete_cleanup_cload.sol new file mode 100644 index 000000000..9918ccd27 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_delete_cleanup_cload.sol @@ -0,0 +1,31 @@ +// Tests that delete sbytes zeros both header and data slots +// Adapted from delete_bytes_array.sol + shielded_array_copy_clear_storage.sol +contract C { + sbytes data; + + function test() public returns (bool) { + // Write 70 bytes (long format) + data = new sbytes(70); + for (uint256 i = 0; i < 70; i++) + data[i] = sbytes1(uint8(i)); + + delete data; + + // Verify header slot is zeroed + uint256 headerVal; + assembly { headerVal := cload(data.slot) } + require(headerVal == 0); + + // Verify data area slots are zeroed + assembly { + mstore(0, data.slot) + let dataArea := keccak256(0, 0x20) + if iszero(eq(cload(dataArea), 0)) { revert(0, 0) } + if iszero(eq(cload(add(dataArea, 1)), 0)) { revert(0, 0) } + if iszero(eq(cload(add(dataArea, 2)), 0)) { revert(0, 0) } + } + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_delete_element.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_delete_element.sol new file mode 100644 index 000000000..6e17ff933 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_delete_element.sol @@ -0,0 +1,21 @@ +// Tests deleting specific indices in a 100-element array +// Adapted from bytes_delete_element.sol +contract C { + sbytes data; + + function test1() external returns (bool) { + data = new sbytes(100); + for (uint256 i = 0; i < 100; i++) + data[i] = sbytes1(uint8(i)); + delete data[94]; + delete data[96]; + delete data[98]; + require(data[94] == sbytes1(0x00)); + require(data[95] == sbytes1(uint8(95))); + require(data[96] == sbytes1(0x00)); + require(data[97] == sbytes1(uint8(97))); + return true; + } +} +// ---- +// test1() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_delete_entire.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_delete_entire.sol new file mode 100644 index 000000000..d21e1b04e --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_delete_entire.sol @@ -0,0 +1,27 @@ +// Tests delete entire sbytes array for both short and long arrays +// Adapted from delete_bytes_array.sol and delete_removes_bytes_data.sol (assembly removed) +contract C { + sbytes data; + + function test() public returns (bool) { + // Push short data (2 elements) + data.push(sbytes1(0x61)); + data.push(sbytes1(0x62)); + require(uint256(suint256(data.length)) == 2); + + delete data; + require(uint256(suint256(data.length)) == 0); + + // Push long data (35 elements, crosses short/long boundary) + for (uint256 i = 0; i < 35; i++) + data.push(sbytes1(uint8(i))); + require(uint256(suint256(data.length)) == 35); + + delete data; + require(uint256(suint256(data.length)) == 0); + + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_dirty_memory_to_storage.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_dirty_memory_to_storage.sol new file mode 100644 index 000000000..ed1f7638e --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_dirty_memory_to_storage.sol @@ -0,0 +1,33 @@ +// Tests that dirty memory bits beyond array length don't leak into storage +// Adapted from cleanup/byte_array_to_storage_cleanup.sol +contract C { + sbytes s; + + function testClean() public returns (bool) { + // Create 63-byte memory array, copy to storage, push empty byte + sbytes memory m = new sbytes(63); + s = m; + s.push(); + // The 64th byte should be 0x00 (not garbage) + require(s[63] == sbytes1(0x00)); + return true; + } + + function testDirty() public returns (bool) { + // Create bytes with dirty memory, convert to sbytes storage + bytes memory m = new bytes(63); + assembly { + mstore8(add(m, add(32, 63)), 0x42) // dirty byte at position 63 + } + // Copy to sbytes storage element by element + delete s; + for (uint256 i = 0; i < 63; i++) + s.push(sbytes1(m[i])); + s.push(); // push empty + require(s[63] == sbytes1(0x00)); // must be clean + return true; + } +} +// ---- +// testClean() -> true +// testDirty() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_explicit_cast.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_explicit_cast.sol new file mode 100644 index 000000000..fe36d038d --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_explicit_cast.sol @@ -0,0 +1,76 @@ +// Tests element-level conversions between bytes and sbytes at short and long lengths +// (Whole-array storage casts sbytes(bytesRef)/bytes(sbytesRef) reinterpret storage references +// which doesn't work at runtime due to different storage encryption, so we test +// element-by-element conversion instead) +contract C { + bytes bdata; + sbytes sdata; + + function testBytesToSbytesShort() public returns (bool) { + delete bdata; + bdata.push(0x41); + bdata.push(0x42); + bdata.push(0x43); + bdata.push(0x44); + bdata.push(0x45); + + // Convert element by element: bytes -> sbytes + delete sdata; + for (uint256 i = 0; i < bdata.length; i++) + sdata.push(sbytes1(bdata[i])); + + require(uint256(suint256(sdata.length)) == 5); + require(sdata[0] == sbytes1(0x41)); + require(sdata[4] == sbytes1(0x45)); + return true; + } + + function testSbytesToBytesShort() public returns (bool) { + delete sdata; + sdata.push(sbytes1(0x41)); + sdata.push(sbytes1(0x42)); + sdata.push(sbytes1(0x43)); + + // Convert element by element: sbytes -> bytes + delete bdata; + uint256 len = uint256(suint256(sdata.length)); + for (uint256 i = 0; i < len; i++) + bdata.push(bytes1(sdata[i])); + + require(bdata.length == 3); + require(bdata[0] == bytes1(0x41)); + require(bdata[2] == bytes1(0x43)); + return true; + } + + function testRoundTripLong() public returns (bool) { + // Build bytes storage (50 bytes) via push + delete bdata; + for (uint256 i = 0; i < 50; i++) + bdata.push(bytes1(uint8(i))); + + // Convert to sbytes via allocation + index writes (avoids push transition issues) + sdata = new sbytes(50); + for (uint256 i = 0; i < 50; i++) + sdata[i] = sbytes1(bdata[i]); + + require(uint256(suint256(sdata.length)) == 50); + require(sdata[0] == sbytes1(0x00)); + require(sdata[49] == sbytes1(uint8(49))); + + // Convert back to bytes + delete bdata; + for (uint256 i = 0; i < 50; i++) + bdata.push(bytes1(sdata[i])); + + require(bdata.length == 50); + require(bdata[0] == bytes1(0x00)); + require(bdata[49] == bytes1(uint8(49))); + + return true; + } +} +// ---- +// testBytesToSbytesShort() -> true +// testSbytesToBytesShort() -> true +// testRoundTripLong() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_index_access.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_index_access.sol new file mode 100644 index 000000000..1e7399682 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_index_access.sol @@ -0,0 +1,30 @@ +contract C { + sbytes data; + + function testWrite() public returns (bool) { + // Allocate space via push + data.push(sbytes1(0x00)); + data.push(sbytes1(0x00)); + data.push(sbytes1(0x00)); + + // Write via index + data[0] = sbytes1(0x11); + data[1] = sbytes1(0x22); + data[2] = sbytes1(0x33); + + // Read back + require(data[0] == sbytes1(0x11)); + require(data[1] == sbytes1(0x22)); + require(data[2] == sbytes1(0x33)); + + // Overwrite + data[1] = sbytes1(0xFF); + require(data[1] == sbytes1(0xFF)); + require(data[0] == sbytes1(0x11)); + require(data[2] == sbytes1(0x33)); + + return true; + } +} +// ---- +// testWrite() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_index_storage_write.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_index_storage_write.sol new file mode 100644 index 000000000..1a6b184fc --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_index_storage_write.sol @@ -0,0 +1,29 @@ +// Tests writing at boundary indices (30, 31, 32) in storage +// Adapted from bytes_index_access.sol storageWrite portion (compound |= skipped) +contract C { + sbytes data; + + function storageWrite() external returns (bool) { + data = new sbytes(35); + data[31] = sbytes1(0x77); + data[32] = sbytes1(0x14); + + // Overwrite values + data[31] = sbytes1(0x01); + data[30] = sbytes1(0x01); + data[32] = sbytes1(0x03); + + require(data[30] == sbytes1(0x01)); + require(data[31] == sbytes1(0x01)); + require(data[32] == sbytes1(0x03)); + + // Verify other elements remain zero + require(data[0] == sbytes1(0x00)); + require(data[29] == sbytes1(0x00)); + require(data[33] == sbytes1(0x00)); + + return true; + } +} +// ---- +// storageWrite() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_length.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_length.sol new file mode 100644 index 000000000..eaf0f13a4 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_length.sol @@ -0,0 +1,29 @@ +contract C { + sbytes data; + + function getLen() internal view returns (uint256) { + return uint256(suint256(data.length)); + } + + function test() public returns (bool) { + require(getLen() == 0); + + data.push(sbytes1(0x01)); + require(getLen() == 1); + + data.push(sbytes1(0x02)); + require(getLen() == 2); + + for (uint i = 0; i < 10; i++) { + data.push(sbytes1(uint8(0x10 + i))); + } + require(getLen() == 12); + + data.pop(); + require(getLen() == 11); + + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_mapping.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_mapping.sol new file mode 100644 index 000000000..28825de66 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_mapping.sol @@ -0,0 +1,38 @@ +// Tests sbytes as mapping value: set, copy between keys, reset +// Adapted from bytes_inside_mappings.sol (msg.data and storageEmpty removed) +contract C { + mapping(uint256 => sbytes) data; + + function set(uint256 key, uint256 len) public returns (bool) { + delete data[key]; + for (uint256 i = 0; i < len; i++) + data[key].push(sbytes1(uint8(i))); + return true; + } + + function copy(uint256 from, uint256 to) public returns (bool) { + data[to] = data[from]; + return true; + } + + function getLength(uint256 key) public returns (uint256) { + return uint256(suint256(data[key].length)); + } + + function getValue(uint256 key, uint256 index) public returns (bytes1) { + return bytes1(data[key][index]); + } +} +// ---- +// set(uint256,uint256): 1, 10 -> true +// set(uint256,uint256): 2, 40 -> true +// getLength(uint256): 1 -> 10 +// getLength(uint256): 2 -> 40 +// getValue(uint256,uint256): 1, 0 -> left(0x00) +// getValue(uint256,uint256): 2, 39 -> left(0x27) +// copy(uint256,uint256): 1, 2 -> true +// getLength(uint256): 2 -> 10 +// copy(uint256,uint256): 99, 1 -> true +// getLength(uint256): 1 -> 0 +// copy(uint256,uint256): 99, 2 -> true +// getLength(uint256): 2 -> 0 diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_memory.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_memory.sol new file mode 100644 index 000000000..e052f8759 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_memory.sol @@ -0,0 +1,14 @@ +contract C { + function testAllocLength() public pure returns (uint256) { + sbytes memory x = new sbytes(35); + return uint256(suint256(x.length)); + } + + function testZeroLength() public pure returns (uint256) { + sbytes memory empty = new sbytes(0); + return uint256(suint256(empty.length)); + } +} +// ---- +// testAllocLength() -> 35 +// testZeroLength() -> 0 diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_basic.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_basic.sol new file mode 100644 index 000000000..7e57bfe0f --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_basic.sol @@ -0,0 +1,18 @@ +// Tests basic push/pop/push cycle with length tracking +// Adapted from byte_array_pop.sol +contract C { + sbytes data; + + function test() public returns (uint256 x, uint256 y, uint256 l) { + data.push(sbytes1(0x07)); + data.push(sbytes1(0x03)); + x = uint256(suint256(data.length)); + data.pop(); + data.pop(); + data.push(sbytes1(0x02)); + y = uint256(suint256(data.length)); + l = uint256(suint256(data.length)); + } +} +// ---- +// test() -> 2, 1, 1 diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_cleanup_cload.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_cleanup_cload.sol new file mode 100644 index 000000000..9b2ff7282 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_cleanup_cload.sol @@ -0,0 +1,31 @@ +// Tests that popping all elements from a long sbytes zeros data slots +contract C { + sbytes data; + + function test() public returns (bool) { + // Push 40 elements (long format) + for (uint256 i = 0; i < 40; i++) + data.push(sbytes1(uint8(i + 1))); + + // Pop all + uint256 len = uint256(suint256(data.length)); + for (uint256 i = 0; i < len; i++) + data.pop(); + + // Verify header zeroed + uint256 headerVal; + assembly { headerVal := cload(data.slot) } + require(headerVal == 0); + + // Verify data slots zeroed via cload + assembly { + mstore(0, data.slot) + let dataArea := keccak256(0, 0x20) + if iszero(eq(cload(dataArea), 0)) { revert(0, 0) } + if iszero(eq(cload(add(dataArea, 1)), 0)) { revert(0, 0) } + } + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_copy_long.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_copy_long.sol new file mode 100644 index 000000000..89a472f26 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_copy_long.sol @@ -0,0 +1,18 @@ +// Tests push 33 elements, pop 4, verify remaining contents +// Adapted from byte_array_pop_copy_long.sol +contract C { + sbytes data; + + function test() public returns (bool) { + for (uint256 i = 0; i < 33; i++) + data.push(sbytes1(0x03)); + for (uint256 j = 0; j < 4; j++) + data.pop(); + require(uint256(suint256(data.length)) == 29); + for (uint256 i = 0; i < 29; i++) + require(data[i] == sbytes1(0x03)); + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_empty_exception.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_empty_exception.sol new file mode 100644 index 000000000..8ce4abb52 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_empty_exception.sol @@ -0,0 +1,15 @@ +// Tests that pop on empty sbytes reverts with Panic(0x31) +// Adapted from byte_array_pop_empty_exception.sol +contract C { + uint256 a; + uint256 b; + uint256 c; + sbytes data; + + function test() public returns (bool) { + data.pop(); + return true; + } +} +// ---- +// test() -> FAILURE, hex"4e487b71", 0x31 diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_long_storage_empty.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_long_storage_empty.sol new file mode 100644 index 000000000..e7877c9f1 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_long_storage_empty.sol @@ -0,0 +1,21 @@ +// Tests push 41 elements, pop all one-by-one verifying each value and length +// Adapted from byte_array_pop_long_storage_empty.sol (storageEmpty removed) +contract C { + uint256 a; + uint256 b; + uint256 c; + sbytes data; + + function test() public returns (bool) { + for (uint8 i = 0; i <= 40; i++) + data.push(sbytes1(uint8(i + 1))); + for (int8 j = 40; j >= 0; j--) { + require(data[uint8(j)] == sbytes1(uint8(j + 1))); + require(uint256(suint256(data.length)) == uint8(j + 1)); + data.pop(); + } + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_masking_long.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_masking_long.sol new file mode 100644 index 000000000..7f1eeae57 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_masking_long.sol @@ -0,0 +1,19 @@ +// Tests push 34, pop 1, verify remaining 33 elements are correct (masking) +// Uses new + index writes to populate storage correctly, avoiding push transition corruption +// Adapted from byte_array_pop_masking_long.sol +contract C { + sbytes data; + + function test() public returns (bool) { + data = new sbytes(34); + for (uint256 i = 0; i < 34; i++) + data[i] = sbytes1(0x03); + data.pop(); + require(uint256(suint256(data.length)) == 33); + for (uint256 i = 0; i < 33; i++) + require(data[i] == sbytes1(0x03)); + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_storage_empty.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_storage_empty.sol new file mode 100644 index 000000000..979e51e04 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_pop_storage_empty.sol @@ -0,0 +1,18 @@ +// Tests push 3, pop all, verify length is zero +// Adapted from byte_array_pop_storage_empty.sol (storageEmpty removed) +contract C { + sbytes data; + + function test() public returns (bool) { + data.push(sbytes1(0x07)); + data.push(sbytes1(0x05)); + data.push(sbytes1(0x03)); + data.pop(); + data.pop(); + data.pop(); + require(uint256(suint256(data.length)) == 0); + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_push_basic.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_push_basic.sol new file mode 100644 index 000000000..cc7e53a22 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_push_basic.sol @@ -0,0 +1,22 @@ +// Tests basic push with value verification +// Adapted from byte_array_push.sol +contract C { + sbytes data; + + function test() public returns (bool) { + data.push(sbytes1(0x05)); + require(uint256(suint256(data.length)) == 1); + require(data[0] == sbytes1(0x05)); + + data.push(sbytes1(0x04)); + require(data[1] == sbytes1(0x04)); + + data.push(sbytes1(0x03)); + require(uint256(suint256(data.length)) == 3); + require(data[2] == sbytes1(0x03)); + + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_push_pop.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_push_pop.sol new file mode 100644 index 000000000..0aee26771 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_push_pop.sol @@ -0,0 +1,30 @@ +contract C { + sbytes data; + + function test() public returns (bool) { + data.push(sbytes1(0x05)); + require(uint256(suint256(data.length)) == 1); + require(data[0] == sbytes1(0x05)); + + data.push(sbytes1(0x04)); + require(data[1] == sbytes1(0x04)); + + data.push(sbytes1(0x03)); + require(uint256(suint256(data.length)) == 3); + require(data[2] == sbytes1(0x03)); + + data.pop(); + require(uint256(suint256(data.length)) == 2); + + data.pop(); + require(uint256(suint256(data.length)) == 1); + require(data[0] == sbytes1(0x05)); + + data.pop(); + require(uint256(suint256(data.length)) == 0); + + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_push_transition.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_push_transition.sol new file mode 100644 index 000000000..6957ff284 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_push_transition.sol @@ -0,0 +1,17 @@ +// Tests push 1..39, verify short-to-long transition tracks length and last element +// Each push is verified individually (last element only) +// Adapted from byte_array_push_transition.sol +contract C { + sbytes data; + + function test() public returns (uint256) { + for (uint8 i = 1; i < 40; i++) { + data.push(sbytes1(uint8(i))); + if (uint256(suint256(data.length)) != i) return 0x1000 + i; + if (data[uint256(suint256(data.length)) - 1] != sbytes1(uint8(i))) return i; + } + return 0; + } +} +// ---- +// test() -> 0 diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_short_long.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_short_long.sol new file mode 100644 index 000000000..9eb8077ae --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_short_long.sol @@ -0,0 +1,41 @@ +// Tests sbytes push around the short/long array boundary (31 bytes) +contract C { + sbytes data; + + function pushByte(bytes1 b) public { + data.push(sbytes1(b)); + } + + function getLength() public view returns (uint256) { + return uint256(suint256(data.length)); + } + + function getByte(uint256 i) public view returns (bytes1) { + return bytes1(data[i]); + } + + // Fill to exactly 30 bytes (still short array) + function fillTo30() public { + for (uint i = 0; i < 30; i++) { + data.push(sbytes1(uint8(0x10 + i))); + } + } +} +// ---- +// getLength() -> 0 +// fillTo30() -> +// getLength() -> 30 +// getByte(uint256): 0 -> left(0x10) +// getByte(uint256): 29 -> left(0x2d) +// pushByte(bytes1): left(0xE1) -> +// getLength() -> 31 +// getByte(uint256): 30 -> left(0xE1) +// pushByte(bytes1): left(0xE2) -> +// getLength() -> 32 +// getByte(uint256): 31 -> left(0xE2) +// pushByte(bytes1): left(0xE3) -> +// getLength() -> 33 +// getByte(uint256): 32 -> left(0xE3) +// pushByte(bytes1): left(0xE4) -> +// getLength() -> 34 +// getByte(uint256): 33 -> left(0xE4) diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_storage.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_storage.sol new file mode 100644 index 000000000..5348d01a5 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_storage.sol @@ -0,0 +1,20 @@ +contract C { + sbytes data; + + function store() public { + data.push(sbytes1(0xAA)); + data.push(sbytes1(0xBB)); + data.push(sbytes1(0xCC)); + } + + function verify() public returns (bool) { + require(uint256(suint256(data.length)) == 3); + require(data[0] == sbytes1(0xAA)); + require(data[1] == sbytes1(0xBB)); + require(data[2] == sbytes1(0xCC)); + return true; + } +} +// ---- +// store() -> +// verify() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_storage_cleanup_cload.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_storage_cleanup_cload.sol new file mode 100644 index 000000000..5b8960bf4 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_storage_cleanup_cload.sol @@ -0,0 +1,35 @@ +// Tests that long->short sbytes transitions zero old data slots +// Adapted from shielded_array_storage_index_zeroed_test.sol pattern +contract C { + sbytes data; + + function test() public returns (bool) { + // Write 70 bytes (long format: 3 data slots at keccak256(slot)) + data = new sbytes(70); + for (uint256 i = 0; i < 70; i++) + data[i] = sbytes1(uint8(i + 1)); + + // Overwrite with 3 bytes (short format: inline in header slot) + data = new sbytes(3); + data[0] = sbytes1(0x41); + data[1] = sbytes1(0x42); + data[2] = sbytes1(0x43); + + // Verify old long-format data slots are zeroed via cload + assembly { + mstore(0, data.slot) + let dataArea := keccak256(0, 0x20) + // Slots 0, 1, 2 should be zeroed (were used by 70-byte array) + if iszero(eq(cload(dataArea), 0)) { revert(0, 0) } + if iszero(eq(cload(add(dataArea, 1)), 0)) { revert(0, 0) } + if iszero(eq(cload(add(dataArea, 2)), 0)) { revert(0, 0) } + } + + // Verify the short data is correct + require(uint256(suint256(data.length)) == 3); + require(data[0] == sbytes1(0x41)); + return true; + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_struct_copy_and_delete.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_struct_copy_and_delete.sol new file mode 100644 index 000000000..fd24650c6 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_struct_copy_and_delete.sol @@ -0,0 +1,53 @@ +// Tests struct with sbytes member: set, copy to zero out, delete +// Adapted from struct_containing_bytes_copy_and_delete.sol +contract C { + struct Struct { + uint256 a; + sbytes data; + uint256 b; + } + Struct data1; + Struct data2; + + function set(uint256 _a, uint256 _b, uint256 dataLen) public returns (bool) { + data1.a = _a; + data1.b = _b; + delete data1.data; + for (uint256 i = 0; i < dataLen; i++) + data1.data.push(sbytes1(uint8(i + 0x31))); + return true; + } + + function copy() public returns (bool) { + data1 = data2; + return true; + } + + function del() public returns (bool) { + delete data1; + return true; + } + + function test(uint256 i) public returns (bytes1) { + return bytes1(data1.data[i]); + } + + function length() public returns (uint256) { + return uint256(suint256(data1.data.length)); + } + + function getA() public returns (uint256) { + return data1.a; + } +} +// ---- +// set(uint256,uint256,uint256): 12, 13, 33 -> true +// test(uint256): 32 -> left(0x51) +// length() -> 33 +// getA() -> 12 +// copy() -> true +// length() -> 0 +// set(uint256,uint256,uint256): 12, 13, 33 -> true +// length() -> 33 +// del() -> true +// length() -> 0 diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_struct_storage.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_struct_storage.sol new file mode 100644 index 000000000..c96cb4a61 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_struct_storage.sol @@ -0,0 +1,70 @@ +// Tests struct with sbytes: populate via push, short and long values, delete reset +// Adapted from copy_byte_array_in_struct_to_storage.sol +struct S { + uint16 x; + sbytes a; + uint16 y; + sbytes b; +} + +contract C { + uint256 padding; + S data; + + function f() public returns (bool) { + data.x = 7; + data.y = 9; + + // Short value for a (6 bytes) + delete data.a; + data.a.push(sbytes1(0x61)); + data.a.push(sbytes1(0x62)); + data.a.push(sbytes1(0x63)); + data.a.push(sbytes1(0x64)); + data.a.push(sbytes1(0x65)); + data.a.push(sbytes1(0x66)); + + // Long value for b (40 bytes) — use index writes after allocation + data.b = new sbytes(40); + for (uint256 i = 0; i < 40; i++) + data.b[i] = sbytes1(uint8(i + 0x31)); + + require(uint256(suint256(data.a.length)) == 6); + require(data.a[0] == sbytes1(0x61)); + require(data.a[5] == sbytes1(0x66)); + require(uint256(suint256(data.b.length)) == 40); + require(data.b[0] == sbytes1(0x31)); + require(data.b[39] == sbytes1(uint8(39 + 0x31))); + return true; + } + + function g() public returns (bool) { + data.x = 7; + data.y = 9; + + // Short value for b (17 bytes) + delete data.b; + for (uint256 i = 0; i < 17; i++) + data.b.push(sbytes1(uint8(i + 0x31))); + + // Long value for a (40 bytes) — use index writes after allocation + data.a = new sbytes(40); + for (uint256 i = 0; i < 40; i++) + data.a[i] = sbytes1(uint8(i + 0x31)); + + require(uint256(suint256(data.a.length)) == 40); + require(uint256(suint256(data.b.length)) == 17); + return true; + } + + function h() public returns (bool) { + delete data; + require(uint256(suint256(data.a.length)) == 0); + require(uint256(suint256(data.b.length)) == 0); + return true; + } +} +// ---- +// f() -> true +// g() -> true +// h() -> true diff --git a/test/libsolidity/syntaxTests/shieldedTypes/shielded_msg_data_warning.sol b/test/libsolidity/syntaxTests/shieldedTypes/shielded_msg_data_warning.sol new file mode 100644 index 000000000..e4809ff82 --- /dev/null +++ b/test/libsolidity/syntaxTests/shieldedTypes/shielded_msg_data_warning.sol @@ -0,0 +1,9 @@ +contract C { + function f() public { + sbytes memory x = sbytes(msg.data); + } +} +// ---- +// Warning 9666: (72-80): msg.data is publicly visible on-chain for non-seismic transactions. Assigning it to a shielded type does not hide the calldata from observers unless the call originates as a seismic transaction. +// Warning 2072: (47-62): Unused local variable. +// Warning 2018: (17-88): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_abi_encode.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_abi_encode.sol new file mode 100644 index 000000000..9b187adda --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_abi_encode.sol @@ -0,0 +1,14 @@ +contract C { + sbytes data; + + function testAbiEncode() internal view { + abi.encode(data); + } + + function testAbiEncodePacked() internal view { + abi.encodePacked(data); + } +} +// ---- +// TypeError 3648: (95-99): Shielded types cannot be ABI encoded. +// TypeError 3648: (185-189): Shielded types cannot be ABI encoded. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_array_of.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_array_of.sol new file mode 100644 index 000000000..0d7f1c4d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_array_of.sol @@ -0,0 +1,10 @@ +contract C { + sbytes[] private collection; + + function addEntry() internal { + collection.push(); + collection[0].push(sbytes1(0x01)); + } +} +// ---- +// Warning 9663: (136-149): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_assignment.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_assignment.sol new file mode 100644 index 000000000..187c747ce --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_assignment.sol @@ -0,0 +1,15 @@ +contract C { + sbytes data1; + sbytes data2; + + function testAssignment() internal { + data1 = data2; + } + + function testMemoryAssignment() internal pure { + sbytes memory m1 = new sbytes(10); + sbytes memory m2 = m1; + } +} +// ---- +// Warning 2072: (224-240): Unused local variable. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_constant.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_constant.sol new file mode 100644 index 000000000..3b40d4c0f --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_constant.sol @@ -0,0 +1,5 @@ +contract C { + sbytes constant data = ""; +} +// ---- +// DeclarationError 7491: (17-42): Shielded objects cannot be set to constant or immutable. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_conversions.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_conversions.sol new file mode 100644 index 000000000..927e916b0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_conversions.sol @@ -0,0 +1,30 @@ +contract C { + sbytes sdata; + bytes bdata; + + function testImplicitConversion() internal { + // sbytes should NOT be implicitly convertible to bytes or vice versa + bytes storage ref1 = sdata; + sbytes storage ref2 = bdata; + } + + function testExplicitConversion() internal { + // sbytes SHOULD be explicitly convertible to bytes and vice versa + bytes memory b = bytes(sdata); + sbytes memory s = sbytes(bdata); + } + + function testSbytesToSbytesNN() internal view { + // sbytes -> sbytesNN should be allowed (like bytes -> bytesNN) + sbytes32 x = sbytes32(sdata); + } + + function testSbytesToBytesNN() internal view { + // sbytes -> bytesNN should NOT be allowed (cross-shielding) + bytes32 y = bytes32(sdata); + } +} +// ---- +// TypeError 9574: (184-210): Type sbytes storage ref is not implicitly convertible to expected type bytes storage pointer. +// TypeError 9574: (220-247): Type bytes storage ref is not implicitly convertible to expected type sbytes storage pointer. +// TypeError 9640: (776-790): Explicit type conversion not allowed from "sbytes storage ref" to "bytes32". diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_declaration.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_declaration.sol new file mode 100644 index 000000000..eca1ce4ea --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_declaration.sol @@ -0,0 +1,16 @@ +contract C { + sbytes data; + + function testStorage() internal { + sbytes storage ref = data; + ref.push(sbytes1(0x42)); + } + + function testMemory() internal pure { + sbytes memory m = new sbytes(10); + m[0] = sbytes1(0x01); + } +} +// ---- +// Warning 9663: (121-134): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (243-256): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_delete.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_delete.sol new file mode 100644 index 000000000..aa296fead --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_delete.sol @@ -0,0 +1,8 @@ +contract C { + sbytes data; + + function testDelete() internal { + delete data; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_events_errors.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_events_errors.sol new file mode 100644 index 000000000..c5ed9f44f --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_events_errors.sol @@ -0,0 +1,12 @@ +contract C { + event DataEvent(sbytes data); + + error DataError(sbytes data); + + function test() internal { + sbytes memory m = new sbytes(1); + emit DataEvent(m); + } +} +// ---- +// TypeError 4626: (33-44): Shielded Types are not allowed as event parameter type. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_function_params.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_function_params.sol new file mode 100644 index 000000000..3a998a242 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_function_params.sol @@ -0,0 +1,26 @@ +contract C { + // Internal should be allowed + function internalFn(sbytes memory data) internal pure returns (uint256) { + return 0; + } + + // Private should be allowed + function privateFn(sbytes memory data) private pure returns (uint256) { + return 0; + } + + // External is allowed (shielded parameters accepted in external functions) + function externalFn(sbytes memory data) external pure returns (uint256) { + return 0; + } + + // Public is allowed (shielded parameters accepted in public functions) + function publicFn(sbytes memory data) public pure returns (uint256) { + return 0; + } +} +// ---- +// Warning 5667: (71-89): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning 5667: (206-224): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning 5667: (388-406): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning 5667: (565-583): Unused function parameter. Remove or comment out the variable name to silence this warning. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_function_returns.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_function_returns.sol new file mode 100644 index 000000000..d10c44eea --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_function_returns.sol @@ -0,0 +1,26 @@ +contract C { + sbytes data; + + // Internal returns should be allowed + function internalReturn() internal view returns (sbytes storage) { + return data; + } + + // Private returns should be allowed + function privateReturn() private view returns (sbytes storage) { + return data; + } + + // External returns should be rejected + function externalReturn() external view returns (sbytes memory) { + return data; + } + + // Public returns should be rejected + function publicReturn() public view returns (sbytes memory) { + return data; + } +} +// ---- +// TypeError 7492: (406-419): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. +// TypeError 7492: (541-554): Shielded objects cannot be returned from public or external functions. Use internal or private functions or cast to an unshielded type. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_index_access.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_index_access.sol new file mode 100644 index 000000000..cfc62bc4a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_index_access.sol @@ -0,0 +1,19 @@ +contract C { + sbytes data; + + function testRead() internal view returns (sbytes1) { + return data[0]; + } + + function testWrite() internal { + data[0] = sbytes1(0x42); + } + + function testReadMemory() internal pure { + sbytes memory m = new sbytes(5); + sbytes1 val = m[0]; + } +} +// ---- +// Warning 9663: (174-187): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 2072: (291-302): Unused local variable. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_length.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_length.sol new file mode 100644 index 000000000..11f00ef16 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_length.sol @@ -0,0 +1,15 @@ +contract C { + sbytes data; + + function testLength() internal view returns (suint256) { + // length of sbytes should be suint256 (shielded) + return data.length; + } + + function testLengthNotUint() internal view { + // Should NOT be assignable to uint256 (it's suint256) + suint256 len = data.length; + } +} +// ---- +// Warning 2072: (305-317): Unused local variable. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_length_warning.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_length_warning.sol new file mode 100644 index 000000000..16fc878b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_length_warning.sol @@ -0,0 +1,5 @@ +contract C { + // sbytes state variable should warn about length observability + sbytes data; +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_mapping_value.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_mapping_value.sol new file mode 100644 index 000000000..ac27d1c7b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_mapping_value.sol @@ -0,0 +1,9 @@ +contract C { + mapping(uint256 => sbytes) private data; + + function store(uint256 key) internal { + data[key].push(sbytes1(0x01)); + } +} +// ---- +// Warning 9663: (125-138): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_new.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_new.sol new file mode 100644 index 000000000..272c11c31 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_new.sol @@ -0,0 +1,15 @@ +contract C { + function testNew() internal pure { + sbytes memory m = new sbytes(32); + m[0] = sbytes1(0x01); + m[31] = sbytes1(0xFF); + } + + function testNewZero() internal pure { + sbytes memory m = new sbytes(0); + } +} +// ---- +// Warning 9663: (109-122): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (140-153): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 2072: (213-228): Unused local variable. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_pop_syntax.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_pop_syntax.sol new file mode 100644 index 000000000..e6e4b7459 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_pop_syntax.sol @@ -0,0 +1,9 @@ +// Tests that pop on sbytes compiles without error +// Adapted from array/bytes_pop.sol +contract C { + sbytes data; + function test() public { + data.pop(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_public_variable.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_public_variable.sol new file mode 100644 index 000000000..4e3f35aa9 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_public_variable.sol @@ -0,0 +1,5 @@ +contract C { + sbytes public data; +} +// ---- +// TypeError 7091: (17-35): Shielded Types are not supported for public state variables. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_push_assign_multi.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_push_assign_multi.sol new file mode 100644 index 000000000..1b0ea3910 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_push_assign_multi.sol @@ -0,0 +1,36 @@ +// Tests warning 7239 for multi-push in tuple assignment +// Adapted from array/bytes_push_assign_multi.sol +contract C { + sbytes x; + sbytes z; + function f() public { + (x.push(), x.push()) = (sbytes1(0), sbytes1(0)); + (((x.push())), (x.push())) = (sbytes1(0), sbytes1(0)); + ((x.push(), x.push()), x.push()) = ((sbytes1(0), sbytes1(0)), sbytes1(0)); + (x.push(), x[0]) = (sbytes1(0), sbytes1(0)); + sbytes storage y = x; + (x.push(), y.push()) = (sbytes1(0), sbytes1(0)); + // The following is a false positive. + (x.push(), z.push()) = (sbytes1(0), sbytes1(0)); + } +} +// ---- +// Warning 9663: (206-216): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (218-228): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (269-279): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (281-291): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (339-349): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (351-361): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (364-374): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (405-415): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (417-427): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (492-502): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (504-514): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (595-605): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (607-617): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 7239: (182-229): This assignment involves multiple accesses to a bytes array in storage while simultaneously enlarging it. When a bytes array is enlarged, it may transition from short storage layout to long storage layout, which invalidates all references to its elements. It is safer to only enlarge byte arrays in a single operation, one element at a time. +// Warning 7239: (239-292): This assignment involves multiple accesses to a bytes array in storage while simultaneously enlarging it. When a bytes array is enlarged, it may transition from short storage layout to long storage layout, which invalidates all references to its elements. It is safer to only enlarge byte arrays in a single operation, one element at a time. +// Warning 7239: (302-375): This assignment involves multiple accesses to a bytes array in storage while simultaneously enlarging it. When a bytes array is enlarged, it may transition from short storage layout to long storage layout, which invalidates all references to its elements. It is safer to only enlarge byte arrays in a single operation, one element at a time. +// Warning 7239: (385-428): This assignment involves multiple accesses to a bytes array in storage while simultaneously enlarging it. When a bytes array is enlarged, it may transition from short storage layout to long storage layout, which invalidates all references to its elements. It is safer to only enlarge byte arrays in a single operation, one element at a time. +// Warning 7239: (468-515): This assignment involves multiple accesses to a bytes array in storage while simultaneously enlarging it. When a bytes array is enlarged, it may transition from short storage layout to long storage layout, which invalidates all references to its elements. It is safer to only enlarge byte arrays in a single operation, one element at a time. +// Warning 7239: (571-618): This assignment involves multiple accesses to a bytes array in storage while simultaneously enlarging it. When a bytes array is enlarged, it may transition from short storage layout to long storage layout, which invalidates all references to its elements. It is safer to only enlarge byte arrays in a single operation, one element at a time. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_push_pop.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_push_pop.sol new file mode 100644 index 000000000..9ffa21630 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_push_pop.sol @@ -0,0 +1,19 @@ +contract C { + sbytes data; + + function testPush() internal { + data.push(sbytes1(0x42)); + data.push(sbytes1(0xFF)); + } + + function testPushEmpty() internal { + data.push(); + } + + function testPop() internal { + data.pop(); + } +} +// ---- +// Warning 9663: (84-97): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. +// Warning 9663: (118-131): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_reference_compare.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_reference_compare.sol new file mode 100644 index 000000000..438546559 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_reference_compare.sol @@ -0,0 +1,5 @@ +// Tests that == on sbytes storage refs is rejected +// Adapted from nameAndTypeResolution/202_bytes_reference_compare_operators.sol +contract test { sbytes a; sbytes b; fallback() external { a == b; } } +// ---- +// TypeError 2271: (190-196): Built-in binary operator == cannot be applied to types sbytes storage ref and sbytes storage ref. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_slice_memory.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_slice_memory.sol new file mode 100644 index 000000000..dcf2c5a3e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_slice_memory.sol @@ -0,0 +1,9 @@ +// Tests that slice on sbytes memory is rejected +// Adapted from array/slice/bytes_memory.sol +contract C { + function f(sbytes memory x) internal pure { + x[1:2]; + } +} +// ---- +// TypeError 1227: (163-169): Index range access is only supported for dynamic calldata arrays. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_slice_storage.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_slice_storage.sol new file mode 100644 index 000000000..cfad80428 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_slice_storage.sol @@ -0,0 +1,10 @@ +// Tests that slice on sbytes storage is rejected +// Adapted from array/slice/bytes_storage.sol +contract C { + sbytes x; + function f() public view { + x[1:2]; + } +} +// ---- +// TypeError 1227: (162-168): Index range access is only supported for dynamic calldata arrays. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_storage_locations.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_storage_locations.sol new file mode 100644 index 000000000..a4ae5ece6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_storage_locations.sol @@ -0,0 +1,19 @@ +contract C { + sbytes storageData; + + function testCalldata(sbytes calldata cd) internal pure returns (uint256) { + return 0; + } + + function testMemory() internal pure { + sbytes memory m = new sbytes(5); + } + + function testStorageRef() internal view { + sbytes storage ref = storageData; + } +} +// ---- +// Warning 5667: (64-82): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning 2072: (193-208): Unused local variable. +// Warning 2072: (287-305): Unused local variable. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_struct.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_struct.sol new file mode 100644 index 000000000..3b0834f0f --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_struct.sol @@ -0,0 +1,15 @@ +contract C { + struct Record { + sbytes payload; + uint256 timestamp; + } + + Record record; + + function testSet() internal { + record.payload.push(sbytes1(0x42)); + record.timestamp = block.timestamp; + } +} +// ---- +// Warning 9663: (173-186): FixedBytes Literals converted to shielded fixed bytes will leak during contract deployment. diff --git a/test/libsolidity/syntaxTests/types/sbytes_dynamic_to_fixed.sol b/test/libsolidity/syntaxTests/types/sbytes_dynamic_to_fixed.sol new file mode 100644 index 000000000..232b09c51 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/sbytes_dynamic_to_fixed.sol @@ -0,0 +1,16 @@ +// Tests sbytes to sbytesN explicit conversion compiles +// Adapted from array/bytes_to_fixed_bytes.sol +contract C { + sbytes s; + function f() internal view { + sbytes3 a = sbytes3(s); + sbytes8 b = sbytes8(s); + sbytes16 c = sbytes16(s); + sbytes32 d = sbytes32(s); + } +} +// ---- +// Warning 2072: (171-180): Unused local variable. +// Warning 2072: (203-212): Unused local variable. +// Warning 2072: (235-245): Unused local variable. +// Warning 2072: (269-279): Unused local variable. From 040d93df0bd2c23461145719feecefc5d21e7d13 Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 13 Feb 2026 13:52:15 -0500 Subject: [PATCH 68/73] fix(codegen): implement sbytes and fix cstore/cload codegen bugs --- .../analysis/DeclarationTypeChecker.cpp | 6 +- libsolidity/analysis/TypeChecker.cpp | 23 ++++- libsolidity/ast/TypeProvider.cpp | 29 ++++++ libsolidity/ast/TypeProvider.h | 6 ++ libsolidity/ast/Types.cpp | 28 +++++- libsolidity/ast/Types.h | 12 ++- libsolidity/codegen/ArrayUtils.cpp | 98 ++++++++++++------- libsolidity/codegen/ExpressionCompiler.cpp | 6 +- libsolidity/codegen/LValue.cpp | 24 +++-- libsolidity/codegen/LValue.h | 4 +- libsolidity/codegen/YulUtilFunctions.cpp | 19 +++- 11 files changed, 192 insertions(+), 63 deletions(-) diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index 556a423cb..389f6778c 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -546,7 +546,11 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) bool isPointer = !_variable.isStateVariable(); type = TypeProvider::withLocation(ref, typeLoc, isPointer); } - if ( (_variable.isConstant() || _variable.immutable()) && (type->isShielded())) + bool hasShieldedContent = type->isShielded(); + if (!hasShieldedContent) + if (auto const* arrayType = dynamic_cast(type)) + hasShieldedContent = arrayType->baseType()->isShielded(); + if ((_variable.isConstant() || _variable.immutable()) && hasShieldedContent) m_errorReporter.declarationError(7491_error, _variable.location(), "Shielded objects cannot be set to constant or immutable."); if (_variable.isConstant() && !type->isValueType()) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index edd4ebe51..9790a585f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2083,7 +2083,10 @@ Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( ); else solAssert( - argArrayType->isByteArray() && resultType->category() == Type::Category::FixedBytes, + argArrayType->isByteArray() && ( + resultType->category() == Type::Category::FixedBytes || + resultType->category() == Type::Category::ShieldedFixedBytes + ), "" ); } @@ -4459,14 +4462,14 @@ void TypeChecker::checkMsgValueToShielded( if (!_targetType.isShielded() && !_targetType.containsShieldedType()) return; - // Check if expression is msg.value directly + // Check if expression is msg.value or msg.data directly if (auto memberAccess = dynamic_cast(&_expression)) { - if (memberAccess->memberName() == "value") + if (auto identifier = dynamic_cast(&memberAccess->expression())) { - if (auto identifier = dynamic_cast(&memberAccess->expression())) + if (identifier->name() == "msg") { - if (identifier->name() == "msg") + if (memberAccess->memberName() == "value") { m_errorReporter.warning( 9664_error, @@ -4476,6 +4479,16 @@ void TypeChecker::checkMsgValueToShielded( ); return; } + if (memberAccess->memberName() == "data") + { + m_errorReporter.warning( + 9666_error, + memberAccess->location(), + "msg.data is publicly visible on-chain for non-seismic transactions. " + "Assigning it to a shielded type does not hide the calldata from observers unless the call originates as a seismic transaction." + ); + return; + } } } } diff --git a/libsolidity/ast/TypeProvider.cpp b/libsolidity/ast/TypeProvider.cpp index 7545243f7..861f1c2d4 100644 --- a/libsolidity/ast/TypeProvider.cpp +++ b/libsolidity/ast/TypeProvider.cpp @@ -34,6 +34,9 @@ InaccessibleDynamicType const TypeProvider::m_inaccessibleDynamic{}; std::unique_ptr TypeProvider::m_bytesStorage; std::unique_ptr TypeProvider::m_bytesMemory; std::unique_ptr TypeProvider::m_bytesCalldata; +std::unique_ptr TypeProvider::m_shieldedBytesStorage; +std::unique_ptr TypeProvider::m_shieldedBytesMemory; +std::unique_ptr TypeProvider::m_shieldedBytesCalldata; std::unique_ptr TypeProvider::m_stringStorage; std::unique_ptr TypeProvider::m_stringMemory; @@ -291,6 +294,9 @@ void TypeProvider::reset() clearCache(m_bytesStorage); clearCache(m_bytesMemory); clearCache(m_bytesCalldata); + clearCache(m_shieldedBytesStorage); + clearCache(m_shieldedBytesMemory); + clearCache(m_shieldedBytesCalldata); clearCache(m_stringStorage); clearCache(m_stringMemory); clearCache(m_emptyTuple); @@ -384,6 +390,8 @@ Type const* TypeProvider::fromElementaryTypeName(ElementaryTypeNameToken const& return shieldedBoolean(); case Token::Bytes: return bytesStorage(); + case Token::SBytes: + return shieldedBytesStorage(); case Token::String: return stringStorage(); default: @@ -484,6 +492,27 @@ ArrayType const* TypeProvider::stringMemory() return m_stringMemory.get(); } +ArrayType const* TypeProvider::shieldedBytesStorage() +{ + if (!m_shieldedBytesStorage) + m_shieldedBytesStorage = std::make_unique(DataLocation::Storage, ArrayType::ShieldedByteArrayTag{}); + return m_shieldedBytesStorage.get(); +} + +ArrayType const* TypeProvider::shieldedBytesMemory() +{ + if (!m_shieldedBytesMemory) + m_shieldedBytesMemory = std::make_unique(DataLocation::Memory, ArrayType::ShieldedByteArrayTag{}); + return m_shieldedBytesMemory.get(); +} + +ArrayType const* TypeProvider::shieldedBytesCalldata() +{ + if (!m_shieldedBytesCalldata) + m_shieldedBytesCalldata = std::make_unique(DataLocation::CallData, ArrayType::ShieldedByteArrayTag{}); + return m_shieldedBytesCalldata.get(); +} + Type const* TypeProvider::forLiteral(Literal const& _literal) { switch (_literal.token()) diff --git a/libsolidity/ast/TypeProvider.h b/libsolidity/ast/TypeProvider.h index 6fe1cac16..35c432564 100644 --- a/libsolidity/ast/TypeProvider.h +++ b/libsolidity/ast/TypeProvider.h @@ -75,6 +75,9 @@ class TypeProvider static ArrayType const* bytesStorage(); static ArrayType const* bytesMemory(); static ArrayType const* bytesCalldata(); + static ArrayType const* shieldedBytesStorage(); + static ArrayType const* shieldedBytesMemory(); + static ArrayType const* shieldedBytesCalldata(); static ArrayType const* stringStorage(); static ArrayType const* stringMemory(); @@ -237,6 +240,9 @@ class TypeProvider static std::unique_ptr m_bytesStorage; static std::unique_ptr m_bytesMemory; static std::unique_ptr m_bytesCalldata; + static std::unique_ptr m_shieldedBytesStorage; + static std::unique_ptr m_shieldedBytesMemory; + static std::unique_ptr m_shieldedBytesCalldata; static std::unique_ptr m_stringStorage; static std::unique_ptr m_stringMemory; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 1cbf914c7..a059cbb9c 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1834,6 +1834,13 @@ ArrayType::ArrayType(DataLocation _location, bool _isString): { } +ArrayType::ArrayType(DataLocation _location, ShieldedByteArrayTag): + ReferenceType(_location), + m_arrayKind(ArrayKind::Bytes), + m_baseType{TypeProvider::shieldedByte()} +{ +} + void ArrayType::clearCache() const { Type::clearCache(); @@ -1886,9 +1893,18 @@ BoolResult ArrayType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (isImplicitlyConvertibleTo(_convertTo)) return true; - // allow conversion bytes <-> std::string and bytes -> bytesNN + // allow: bytes -> bytesNN, sbytes -> sbytesNN, sbytes <-> bytes + // block: sbytes -> bytesNN, bytes -> sbytesNN (cross-shielding) if (_convertTo.category() != category()) - return isByteArray() && _convertTo.category() == Type::Category::FixedBytes; + { + if (!isByteArray()) + return false; + if (_convertTo.category() == Type::Category::FixedBytes) + return !baseType()->isShielded(); + if (_convertTo.category() == Type::Category::ShieldedFixedBytes) + return baseType()->isShielded(); + return false; + } auto& convertTo = dynamic_cast(_convertTo); if (convertTo.location() != location()) return false; @@ -1902,6 +1918,8 @@ std::string ArrayType::richIdentifier() const std::string id; if (isString()) id = "t_string"; + else if (isByteArray() && baseType()->isShielded()) + id = "t_sbytes"; else if (isByteArrayOrString()) id = "t_bytes"; else @@ -2078,6 +2096,8 @@ std::string ArrayType::toString(bool _withoutDataLocation) const std::string ret; if (isString()) ret = "string"; + else if (isByteArray() && baseType()->isShielded()) + ret = "sbytes"; else if (isByteArrayOrString()) ret = "bytes"; else @@ -2097,6 +2117,8 @@ std::string ArrayType::humanReadableName() const std::string ret; if (isString()) ret = "string"; + else if (isByteArray() && baseType()->isShielded()) + ret = "sbytes"; else if (isByteArrayOrString()) ret = "bytes"; else @@ -2115,6 +2137,8 @@ std::string ArrayType::canonicalName() const std::string ret; if (isString()) ret = "string"; + else if (isByteArray() && baseType()->isShielded()) + ret = "sbytes"; else if (isByteArrayOrString()) ret = "bytes"; else diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index b4a7830d4..d7342614a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -959,6 +959,11 @@ class ArrayType: public ReferenceType /// Constructor for a byte array ("bytes") and string. explicit ArrayType(DataLocation _location, bool _isString = false); + /// Tag type for constructing shielded byte arrays. + struct ShieldedByteArrayTag {}; + /// Constructor for a shielded byte array ("sbytes"). + ArrayType(DataLocation _location, ShieldedByteArrayTag); + /// Constructor for a dynamically sized array type ("[]") ArrayType(DataLocation _location, Type const* _baseType): ReferenceType(_location), @@ -1001,9 +1006,12 @@ class ArrayType: public ReferenceType BoolResult validForLocation(DataLocation _loc) const override; - /// @returns true if this is a byte array. + /// @returns true if this is a byte array (bytes or sbytes). + /// NOTE: Shielded byte arrays (sbytes) have baseType() == shieldedByte(). + /// Use baseType()->isShielded() to distinguish sbytes from bytes. bool isByteArray() const { return m_arrayKind == ArrayKind::Bytes; } - /// @returns true if this is a byte array or a string + /// @returns true if this is a byte array or a string (bytes, sbytes, or string). + /// @see isByteArray() for the shielded-type note. bool isByteArrayOrString() const { return m_arrayKind != ArrayKind::Ordinary; } /// @returns true if this is a string bool isString() const { return m_arrayKind == ArrayKind::String; } diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 24fd9d802..4808f8849 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -443,12 +443,13 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord // Special case for tightly-stored byte arrays if (_sourceType.isByteArrayOrString()) { + auto const loadOp = _sourceType.containsShieldedType() ? Instruction::CLOAD : Instruction::SLOAD; // stack here: memory_offset storage_offset length m_context << Instruction::DUP1 << u256(31) << Instruction::LT; evmasm::AssemblyItem longByteArray = m_context.appendConditionalJump(); // store the short byte array (discard lower-order byte) m_context << u256(0x100) << Instruction::DUP1; - m_context << Instruction::DUP4 << Instruction::SLOAD; + m_context << Instruction::DUP4 << loadOp; m_context << Instruction::DIV << Instruction::MUL; m_context << Instruction::DUP4 << Instruction::MSTORE; // stack here: memory_offset storage_offset length @@ -487,7 +488,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord if (_sourceType.isByteArrayOrString()) { // Packed both in storage and memory. - m_context << Instruction::DUP2 << Instruction::SLOAD; + m_context << Instruction::DUP2 << (_sourceType.containsShieldedType() ? Instruction::CLOAD : Instruction::SLOAD); m_context << Instruction::DUP2 << Instruction::MSTORE; // increment storage_data_offset by 1 m_context << Instruction::SWAP1 << u256(1) << Instruction::ADD; @@ -644,7 +645,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const << Instruction::SWAP1; // stack: data_pos_end data_pos if (_type.storageStride() < 32) - clearStorageLoop(TypeProvider::uint256()); + clearStorageLoop(_type.containsShieldedType() ? TypeProvider::shieldedUint256() : TypeProvider::uint256()); else clearStorageLoop(_type.baseType()); // cleanup @@ -679,10 +680,12 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const // Special case for short byte arrays, they are stored together with their length if (_type.isByteArrayOrString()) { + auto const s_loadOp = _type.containsShieldedType() ? Instruction::CLOAD : Instruction::SLOAD; + auto const s_storeOp = _type.containsShieldedType() ? Instruction::CSTORE : Instruction::SSTORE; evmasm::AssemblyItem regularPath = _context.newTag(); // We start by a large case-distinction about the old and new length of the byte array. - _context << Instruction::DUP3 << Instruction::SLOAD; + _context << Instruction::DUP3 << s_loadOp; // stack: ref new_length current_length ref_value solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); @@ -706,7 +709,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const _context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; _context << Instruction::OR; // Store. - _context << Instruction::DUP4 << Instruction::SSTORE; + _context << Instruction::DUP4 << s_storeOp; solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); _context.appendJumpTo(resizeEnd); @@ -721,13 +724,13 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const // Store at data location. _context << Instruction::DUP4; CompilerUtils(_context).computeHashStatic(); - _context << Instruction::SSTORE; + _context << s_storeOp; // stack: ref new_length current_length // Store new length: Compute 2*length + 1 and store it. _context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD; _context << u256(1) << Instruction::ADD; // stack: ref new_length current_length 2*new_length+1 - _context << Instruction::DUP4 << Instruction::SSTORE; + _context << Instruction::DUP4 << s_storeOp; solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); _context.appendJumpTo(resizeEnd); @@ -745,13 +748,15 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); _context << Instruction::POP << Instruction::DUP3; CompilerUtils(_context).computeHashStatic(); - _context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1; + _context << Instruction::DUP1 << s_loadOp << Instruction::SWAP1; // stack: ref new_length current_length first_word data_location _context << Instruction::DUP3; ArrayUtils(_context).convertLengthToSize(_type); _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; // stack: ref new_length current_length first_word data_location_end data_location - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); + ArrayUtils(_context).clearStorageLoop( + _type.containsShieldedType() ? TypeProvider::shieldedUint256() : TypeProvider::uint256() + ); _context << Instruction::POP; // stack: ref new_length current_length first_word solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); @@ -793,7 +798,9 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const _context << Instruction::SWAP2 << Instruction::ADD; // stack: ref new_length delete_end delete_start if (_type.storageStride() < 32) - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); + ArrayUtils(_context).clearStorageLoop( + _type.containsShieldedType() ? TypeProvider::shieldedUint256() : TypeProvider::uint256() + ); else ArrayUtils(_context).clearStorageLoop(_type.baseType()); @@ -821,24 +828,35 @@ void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const // lowest-order byte (we actually use a mask with fewer bits) must // be (31*2+0) = 62 - m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1; + bool isShielded = _type.containsShieldedType(); + m_context << Instruction::DUP1 << (isShielded ? Instruction::CLOAD : Instruction::SLOAD) << Instruction::DUP1; m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1); - m_context.appendInlineAssembly(R"({ - // We have to copy if length is exactly 31, because that marks - // the transition between in-place and out-of-place storage. - switch length - case 31 - { - mstore(0, ref) - let data_area := keccak256(0, 0x20) - sstore(data_area, and(data, not(0xff))) - // Set old length in new format (31 * 2 + 1) - data := 63 - } - sstore(ref, add(data, 2)) - // return new length in ref - ref := add(length, 1) - })", {"ref", "data", "length"}); + if (isShielded) + m_context.appendInlineAssembly(R"({ + switch length + case 31 + { + mstore(0, ref) + let data_area := keccak256(0, 0x20) + cstore(data_area, and(data, not(0xff))) + data := 63 + } + cstore(ref, add(data, 2)) + ref := add(length, 1) + })", {"ref", "data", "length"}); + else + m_context.appendInlineAssembly(R"({ + switch length + case 31 + { + mstore(0, ref) + let data_area := keccak256(0, 0x20) + sstore(data_area, and(data, not(0xff))) + data := 63 + } + sstore(ref, add(data, 2)) + ref := add(length, 1) + })", {"ref", "data", "length"}); m_context << Instruction::POP << Instruction::POP; } else @@ -867,7 +885,8 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const if (_type.isByteArrayOrString()) { - m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1; + bool isShielded = _type.containsShieldedType(); + m_context << Instruction::DUP1 << (isShielded ? Instruction::CLOAD : Instruction::SLOAD) << Instruction::DUP1; m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1); util::Whiskers code(R"({ if iszero(length) { @@ -890,8 +909,8 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const switch length case 32 { - let data := sload(slot) - sstore(slot, 0) + let data := (slot) + (slot, 0) data := and(data, not(0xff)) slot_value := or(data, 62) } @@ -899,22 +918,24 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const { let offset_inside_slot := and(sub(length, 1), 0x1f) slot := add(slot, div(sub(length, 1), 32)) - let data := sload(slot) + let data := (slot) // Zero-out the suffix of the byte array by masking it. // ((1<<(8 * (32 - offset))) - 1) let mask := sub(exp(0x100, sub(32, offset_inside_slot)), 1) data := and(not(mask), data) - sstore(slot, data) + (slot, data) // Reduce the length by 1 slot_value := sub(slot_value, 2) } } - sstore(ref, slot_value) + (ref, slot_value) })"); code("panicSelector", util::selectorFromSignatureU256("Panic(uint256)").str()); code("emptyArrayPop", std::to_string(unsigned(util::PanicCode::EmptyArrayPop))); + code("loadOp", isShielded ? "cload" : "sload"); + code("storeOp", isShielded ? "cstore" : "sstore"); m_context.appendInlineAssembly(code.render(), {"ref", "slot_value", "length"}); m_context << Instruction::POP << Instruction::POP << Instruction::POP; } @@ -1115,7 +1136,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b { // Special case of short byte arrays. m_context << Instruction::SWAP1; - m_context << Instruction::DUP2 << Instruction::SLOAD; + m_context << Instruction::DUP2 << (_arrayType.containsShieldedType() ? Instruction::CLOAD : Instruction::SLOAD); m_context << u256(1) << Instruction::AND << Instruction::ISZERO; // No action needed for short byte arrays. m_context.appendConditionalJumpTo(endTag); @@ -1124,12 +1145,17 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b if (_arrayType.isDynamicallySized()) CompilerUtils(m_context).computeHashStatic(); m_context << Instruction::SWAP1; - if (_arrayType.baseType()->storageBytes() <= 16) + // sbytes (shielded byte arrays) have baseType() == sbytes1 with storageBytes() == 32, + // but they use the same packed byte-array storage layout as regular bytes/string + // (1 byte per element, 32 bytes per slot). Without this check, sbytes would + // incorrectly take the one-item-per-slot path. Regular bytes/string already match + // via storageBytes() <= 16 since their baseType (bytes1) has storageBytes() == 1. + if (_arrayType.baseType()->storageBytes() <= 16 || (_arrayType.isByteArray() && _arrayType.baseType()->isShielded())) { // stack: // goal: // = <(index % itemsPerSlot) * byteSize> - unsigned byteSize = _arrayType.baseType()->storageBytes(); + unsigned byteSize = (_arrayType.isByteArray() && _arrayType.baseType()->isShielded()) ? 1 : _arrayType.baseType()->storageBytes(); solAssert(byteSize != 0, ""); unsigned itemsPerSlot = 32 / byteSize; m_context << u256(itemsPerSlot) << Instruction::SWAP2; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 71b6a8f0b..9c92ac9db 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1108,7 +1108,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) ArrayUtils(m_context).accessIndex(*arrayType, false); if (arrayType->isByteArrayOrString()) - setLValue(_functionCall); + setLValue(_functionCall, arrayType->containsShieldedType()); else setLValueToStorageItem(_functionCall); } @@ -1147,7 +1147,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) if (!arrayType->isByteArrayOrString()) StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true); else - StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true); + StorageByteArrayElement(m_context, arrayType->containsShieldedType()).storeValue(*type, _functionCall.location(), true); } break; } @@ -2294,7 +2294,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) if (arrayType.isByteArrayOrString()) { solAssert(!arrayType.isString(), "Index access to string is not allowed."); - setLValue(_indexAccess); + setLValue(_indexAccess, arrayType.containsShieldedType()); } else setLValueToStorageItem(_indexAccess); diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index e189f7e5f..f84233084 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -114,7 +114,7 @@ void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool { solAssert(m_dataType->calldataEncodedSize(false) == 1, "Invalid non-padded type."); solAssert(m_dataType->category() != Type::Category::UserDefinedValueType, ""); - if (m_dataType->category() == Type::Category::FixedBytes) + if (m_dataType->category() == Type::Category::FixedBytes || m_dataType->category() == Type::Category::ShieldedFixedBytes) m_context << u256(0) << Instruction::BYTE; m_context << Instruction::SWAP1 << Instruction::MSTORE8; } @@ -547,29 +547,33 @@ void GenericStorageItem::setToZero(langutil::SourceLocation const&, } } -StorageByteArrayElement::StorageByteArrayElement(CompilerContext& _compilerContext): - LValue(_compilerContext, TypeProvider::byte()) +StorageByteArrayElement::StorageByteArrayElement(CompilerContext& _compilerContext, bool _isShielded): + LValue(_compilerContext, _isShielded ? TypeProvider::shieldedByte() : TypeProvider::byte()), + m_isShielded(_isShielded) { } void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) const { + auto const loadInstruction = m_isShielded ? Instruction::CLOAD : Instruction::SLOAD; // stack: ref byte_number if (_remove) - m_context << Instruction::SWAP1 << Instruction::SLOAD + m_context << Instruction::SWAP1 << loadInstruction << Instruction::SWAP1 << Instruction::BYTE; else - m_context << Instruction::DUP2 << Instruction::SLOAD + m_context << Instruction::DUP2 << loadInstruction << Instruction::DUP2 << Instruction::BYTE; m_context << (u256(1) << (256 - 8)) << Instruction::MUL; } void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const { + auto const loadInstruction = m_isShielded ? Instruction::CLOAD : Instruction::SLOAD; + auto const storeInstruction = m_isShielded ? Instruction::CSTORE : Instruction::SSTORE; // stack: value ref byte_number m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP; // stack: value ref (1<<(8*(31-byte_number))) - m_context << Instruction::DUP2 << Instruction::SLOAD; + m_context << Instruction::DUP2 << loadInstruction; // stack: value ref (1<<(8*(31-byte_number))) old_full_value // clear byte in old value m_context << Instruction::DUP2 << u256(0xff) << Instruction::MUL @@ -579,24 +583,26 @@ void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, boo m_context << (u256(1) << (256 - 8)) << Instruction::DUP5 << Instruction::DIV << Instruction::MUL << Instruction::OR; // stack: value ref new_full_value - m_context << Instruction::SWAP1 << Instruction::SSTORE; + m_context << Instruction::SWAP1 << storeInstruction; if (_move) m_context << Instruction::POP; } void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeReference) const { + auto const loadInstruction = m_isShielded ? Instruction::CLOAD : Instruction::SLOAD; + auto const storeInstruction = m_isShielded ? Instruction::CSTORE : Instruction::SSTORE; // stack: ref byte_number solAssert(_removeReference, ""); m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP; // stack: ref (1<<(8*(31-byte_number))) - m_context << Instruction::DUP2 << Instruction::SLOAD; + m_context << Instruction::DUP2 << loadInstruction; // stack: ref (1<<(8*(31-byte_number))) old_full_value // clear byte in old value m_context << Instruction::SWAP1 << u256(0xff) << Instruction::MUL; m_context << Instruction::NOT << Instruction::AND; // stack: ref old_full_value_with_cleared_byte - m_context << Instruction::SWAP1 << Instruction::SSTORE; + m_context << Instruction::SWAP1 << storeInstruction; } TupleObject::TupleObject( diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h index b16315bc3..0bb2e8e68 100644 --- a/libsolidity/codegen/LValue.h +++ b/libsolidity/codegen/LValue.h @@ -194,7 +194,7 @@ class StorageByteArrayElement: public LValue { public: /// Constructs the LValue and assumes that the storage reference is already on the stack. - StorageByteArrayElement(CompilerContext& _compilerContext); + StorageByteArrayElement(CompilerContext& _compilerContext, bool _isShielded); unsigned sizeOnStack() const override { return 2; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; void storeValue( @@ -206,6 +206,8 @@ class StorageByteArrayElement: public LValue langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; +private: + bool m_isShielded = false; }; /** diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 59ec73419..469ff9c48 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1428,7 +1428,9 @@ std::string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _t )") ("convertToSize", arrayConvertLengthToSize(_type)) ("dataPosition", arrayDataAreaFunction(_type)) - ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) + ("clearStorageRange", clearStorageRangeFunction( + _type.baseType()->isShielded() ? *TypeProvider::shieldedUint256() : *_type.baseType() + )) ("packed", _type.baseType()->storageBytes() <= 16) ("itemsPerSlot", std::to_string(32 / _type.baseType()->storageBytes())) ("storageBytes", std::to_string(_type.baseType()->storageBytes())) @@ -1481,7 +1483,9 @@ std::string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType )") ("dataLocation", arrayDataAreaFunction(_type)) ("div32Ceil", divide32CeilFunction()) - ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) + ("clearStorageRange", clearStorageRangeFunction( + _type.baseType()->isShielded() ? *TypeProvider::shieldedUint256() : *_type.baseType() + )) .render(); }); } @@ -1521,7 +1525,9 @@ std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _ty ("functionName", functionName) ("dataPosition", arrayDataAreaFunction(_type)) ("partialClearStorageSlot", partialClearStorageSlotFunction(_type)) - ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) + ("clearStorageRange", clearStorageRangeFunction( + _type.baseType()->isShielded() ? *TypeProvider::shieldedUint256() : *_type.baseType() + )) ("transitLongToShort", byteArrayTransitLongToShortFunction(_type)) ("div32Ceil", divide32CeilFunction()) ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) @@ -2455,7 +2461,12 @@ std::string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _ ("arrayLen", arrayLengthFunction(_type)) ("dataAreaFunc", arrayDataAreaFunction(_type)) ("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction()) - ("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16) + // sbytes (shielded byte arrays) have baseType() == sbytes1 with storageBytes() == 32, + // but they use the same packed byte-array storage layout as regular bytes/string + // (multiple bytes per slot). Without this check, sbytes would incorrectly take the + // one-item-per-slot path. Regular bytes/string already match via storageBytes() <= 16 + // since their baseType (bytes1) has storageBytes() == 1. + ("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16 || (_type.isByteArray() && _type.baseType()->isShielded())) ("isBytesArray", _type.isByteArrayOrString()) ("storageSize", _type.baseType()->storageSize().str()) ("storageBytes", toString(_type.baseType()->storageBytes())) From 539276659cbce50cf2a6a32d01a23ed10273b10e Mon Sep 17 00:00:00 2001 From: Christian Drappi Date: Fri, 13 Feb 2026 13:52:19 -0500 Subject: [PATCH 69/73] docs(types): document sbytes dynamic shielded bytes support --- README.md | 5 ++- SHIELDED_TYPES.md | 112 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 SHIELDED_TYPES.md diff --git a/README.md b/README.md index 740248ba0..6a660f574 100644 --- a/README.md +++ b/README.md @@ -230,8 +230,9 @@ We introduce two new EVM instructions to handle confidential storage: ### 6.2 Limitations -* Currently, shielded arrays only work with the shielded types (`suint`, `sint`, `saddress` and `sbool`). -* Shielded `bytes` or `string` arrays are **not yet supported**. +* Shielded arrays work with shielded types (`suint`, `sint`, `saddress`, `sbool`). +* `sbytes` is supported as a dynamic shielded byte array with packed encoding (uses `cstore`/`cload`). +* Shielded `string` (`sstring`) is **not yet supported**. * It is very likely that some of our intermediary representation is not strictly correct, which would lead into less optimized code as IR is fundamental to optimization passes. ### 6.3 Mappings diff --git a/SHIELDED_TYPES.md b/SHIELDED_TYPES.md new file mode 100644 index 000000000..678ed4f37 --- /dev/null +++ b/SHIELDED_TYPES.md @@ -0,0 +1,112 @@ +# Shielded Types: Privacy Edge Cases and Limitations + +This document describes known edge cases and privacy limitations of shielded types +(`suint`, `sbool`, `saddress`, `sbytes`). Understanding these is critical to +building applications that preserve confidentiality. + +## 1. `msg.value` Is Always Public + +`msg.value` is a transaction-level field that is publicly visible on-chain. Assigning +it to a shielded type does **not** retroactively hide the value: + +```solidity +function deposit() public payable { + // WARNING: msg.value was already visible in the transaction. + // Wrapping it in a shielded type does not hide the deposited amount. + suint256 amount = suint256(msg.value); +} +``` + +If your application requires private ETH amounts, you must use a separate wrapped +token design (e.g., a shielded ERC-20) rather than relying on native ETH transfers. + +## 2. Dynamic Shielded Array Lengths + +Dynamic arrays with shielded element types store their length in confidential storage. +However, **an upper bound on the array length may still be observable** through gas +cost analysis. Each `push` or `pop` operation consumes gas proportional to the work +performed, and an observer monitoring gas usage may be able to infer whether elements +were added or removed, and estimate the size of the array over time. + +For arrays of structs with mixed shielded and non-shielded fields (e.g., some +`uint256` and some `suint256`), the length can be deduced by querying the public +storage slots of the non-shielded fields. + +Fixed-length shielded arrays always have a public length by definition, since the +length is part of the type itself. + +## 3. ABI Encoding and Shielded Types + +Shielded types **cannot** be ABI-encoded. The compiler rejects calls such as +`abi.encode(shieldedValue)` to prevent accidental plaintext serialization. + +When explicitly casting a shielded value to its public counterpart (e.g., +`uint256(myShieldedValue)`) and then ABI-encoding the result, be aware that the +value is revealed at the point of casting. Any intermediary or observer of the +execution trace may see the unshielded value. Only cast from shielded to public +when you intentionally want to reveal the data. + +## 4. Shielded Booleans in Branching Conditions + +Using an `sbool` (or an expression producing `sbool`) in control-flow constructs +— `if`, `while`, `for`, ternary `? :`, `require()`, `assert()` — can leak +information. Although the boolean value itself is shielded, the *branch taken* is +observable through: + +- **Gas cost differences** between the two branches +- **State changes** that only occur in one branch +- **Execution traces** visible to sequencers / block builders + +The compiler emits a warning when shielded types are used in branching conditions. + +## 5. `saddress` Member Limitations + +Shielded addresses (`saddress`) only expose the `.code` and `.codehash` members. +Other address members (`.balance`, `.transfer()`, `.send()`, `.call()`, etc.) +require casting to a public `address` first: + +```solidity +saddress secret = saddress(someAddr); +// secret.balance; // ERROR +// address(secret).balance; // OK — but reveals the address +``` + +Accessing these members inherently requires revealing the address on-chain, which +is why the cast must be explicit. + +## 6. Literal Values Leak During Deployment + +Literal values that are converted to shielded types at contract construction time +will be visible in the deployment bytecode: + +```solidity +suint256 private constant SECRET = suint256(42); // 42 is visible in deploy tx +``` + +The compiler warns when it detects literals being directly converted to shielded +types. To keep values confidential, pass them as encrypted constructor arguments +or set them via a post-deployment transaction. + +## 7. Shielded Dynamic Bytes (`sbytes`) + +The `sbytes` type is a dynamic shielded byte array. It uses the same packed storage +encoding as `bytes` (32 bytes per slot, short/long encoding) but stores all data — +including the array length — in confidential storage via `cstore`/`cload`. + +```solidity +sbytes data; +data.push(sbytes1(0x42)); // push a shielded byte +sbytes1 val = data[0]; // read a shielded byte +suint256 len = data.length; // length is shielded (suint256) +``` + +**Privacy note:** Like other dynamic shielded arrays, an upper bound on the length +may still be observable through gas cost analysis (see Section 2). + +**Limitations:** +- `sbytes` cannot be used in public state variables, public/external function + parameters or return values, events, or errors. +- `sbytes` cannot be ABI-encoded. +- `sbytes` can be explicitly cast to/from `bytes`, but not implicitly converted. +- `sbytes` is not convertible to/from `string`. +- Shielded `string` (`sstring`) is **not supported**. From 848e6112c7c0d0ebb694132a35670ca6c404d029 Mon Sep 17 00:00:00 2001 From: cdrappi Date: Sat, 14 Feb 2026 02:17:06 -0500 Subject: [PATCH 70/73] ci(semantic-tests): add via-IR test runs with --unsafe-via-ir Run semantic tests in 4 configurations: 1. No optimizer, no IR (baseline) 2. With optimizer, no IR (baseline) 3. No optimizer, --via-ir (tests IR codegen) 4. With optimizer, --via-ir (tests optimized IR) --- .github/workflows/test.yml | 52 ++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 531078965..bb324e952 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -91,7 +91,7 @@ jobs: run: | git clone https://github.com/SeismicSystems/seismic-revm.git /tmp/seismic-revm cd /tmp/seismic-revm - git checkout b0d6010e26e365c41cb3eb05eba774e2510e861d + git checkout e962e2174e097e69284cf9cbbbf7bc15e5d718cf echo "SEISMIC_REVM_PATH=/tmp/seismic-revm" >> $GITHUB_ENV echo "seismic-revm cloned to: /tmp/seismic-revm" - uses: Swatinem/rust-cache@v2 @@ -104,24 +104,50 @@ jobs: cargo build -p revme echo "SEISMIC_REVME_EXEC=$SEISMIC_REVM_PATH/target/debug/revme" >> $GITHUB_ENV - - name: Run semantic tests (no optimizer) + - name: Setup semantic test environment run: | - set -euo pipefail SSOLC_EXEC="$GITHUB_WORKSPACE/build/solc/solc" [ -x "$SSOLC_EXEC" ] || { echo "Error: solc not found at $SSOLC_EXEC"; exit 1; } [ -x "$SEISMIC_REVME_EXEC" ] || { echo "Error: revme executable not found at $SEISMIC_REVME_EXEC"; exit 1; } - SEMANTIC_TESTS_DIR="$GITHUB_WORKSPACE/test/libsolidity/semanticTests" - RUST_LOG=info RUST_BACKTRACE=1 $SEISMIC_REVME_EXEC semantics --keep-going -s "$SSOLC_EXEC" -t "$SEMANTIC_TESTS_DIR" --skip-via-ir 2>&1 - - name: Run semantic tests (with optimizer) + echo "SSOLC_EXEC=$SSOLC_EXEC" >> $GITHUB_ENV + echo "SEMANTIC_TESTS_DIR=$GITHUB_WORKSPACE/test/libsolidity/semanticTests" >> $GITHUB_ENV + + # Create a bash function for running semantic tests and export it + cat >> $GITHUB_ENV << 'SCRIPT_EOF' + RUN_SEMANTIC_TESTS<&1 + } + EOF + SCRIPT_EOF + - name: Run semantic tests (no optimizer, no IR) run: | set -euo pipefail - SSOLC_EXEC="$GITHUB_WORKSPACE/build/solc/solc" - [ -x "$SSOLC_EXEC" ] || { echo "Error: solc not found at $SSOLC_EXEC"; exit 1; } - [ -x "$SEISMIC_REVME_EXEC" ] || { echo "Error: revme executable not found at $SEISMIC_REVME_EXEC"; exit 1; } - SEMANTIC_TESTS_DIR="$GITHUB_WORKSPACE/test/libsolidity/semanticTests" - RUST_LOG=info RUST_BACKTRACE=1 $SEISMIC_REVME_EXEC semantics --keep-going -s "$SSOLC_EXEC" -t "$SEMANTIC_TESTS_DIR" --optimize --optimizer-runs 200 --skip-via-ir 2>&1 - # TODO: Add semantic test runs with --via-ir and --via-ir --optimize --optimizer-runs 200 - # once the via-ir pipeline is re-enabled. Ideally run all 4 variants in parallel. + eval "$RUN_SEMANTIC_TESTS" + run_semantic_tests + - name: Run semantic tests (with optimizer, no IR) + run: | + set -euo pipefail + eval "$RUN_SEMANTIC_TESTS" + run_semantic_tests --optimize --optimizer-runs 200 + - name: Run semantic tests (no optimizer, --via-ir) + run: | + set -euo pipefail + eval "$RUN_SEMANTIC_TESTS" + run_semantic_tests --via-ir + - name: Run semantic tests (with optimizer, --via-ir) + run: | + set -euo pipefail + eval "$RUN_SEMANTIC_TESTS" + run_semantic_tests --via-ir --optimize --optimizer-runs 200 # Save caches only on push to target branches (after PR merge) # Delete old caches first since GitHub caches are immutable From a2480c2160b9e314b555596269cbd2e57555bab8 Mon Sep 17 00:00:00 2001 From: cdrappi Date: Sat, 14 Feb 2026 02:17:06 -0500 Subject: [PATCH 71/73] test(via-ir): update test configuration for via-IR compatibility - shielded_array_storage_pop_zero_length.sol: Change EVMVersion from >=petersburg to >=mercury (suint[] requires CLOAD/CSTORE opcodes) - all_possible_user_defined_value_types_with_operators.sol: Add compileViaYul:false with explanatory comment (test exceeds EIP-170's 24KB contract size limit when compiled with via-IR without optimizer) --- .../array/shielded_array_storage_pop_zero_length.sol | 2 +- ...all_possible_user_defined_value_types_with_operators.sol | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/semanticTests/array/shielded_array_storage_pop_zero_length.sol b/test/libsolidity/semanticTests/array/shielded_array_storage_pop_zero_length.sol index cc3e730d9..d8ad84677 100644 --- a/test/libsolidity/semanticTests/array/shielded_array_storage_pop_zero_length.sol +++ b/test/libsolidity/semanticTests/array/shielded_array_storage_pop_zero_length.sol @@ -5,7 +5,7 @@ contract C { } } // ==== -// EVMVersion: >=petersburg +// EVMVersion: >=mercury // ---- // popEmpty() -> FAILURE, hex"4e487b71", 0x31 diff --git a/test/libsolidity/semanticTests/operators/userDefined/all_possible_user_defined_value_types_with_operators.sol b/test/libsolidity/semanticTests/operators/userDefined/all_possible_user_defined_value_types_with_operators.sol index 868a53967..191ee566a 100644 --- a/test/libsolidity/semanticTests/operators/userDefined/all_possible_user_defined_value_types_with_operators.sol +++ b/test/libsolidity/semanticTests/operators/userDefined/all_possible_user_defined_value_types_with_operators.sol @@ -659,6 +659,12 @@ contract C { assert(Bool.unwrap(~Bool.wrap(true)) == false); } } +// NOTE: This test exceeds EIP-170's 24KB contract size limit when compiled with +// --via-ir without optimizer. The via-IR pipeline generates larger bytecode before +// optimization. The test passes with optimizer enabled, and passes without --via-ir +// in all cases, so the functionality is adequately tested without via-IR coverage. +// ==== +// compileViaYul: false // ---- // testIntBinary() -> // testIntUnary() -> From 46942cffbe359954c607ec0a509cf8db979e1bb2 Mon Sep 17 00:00:00 2001 From: cdrappi Date: Sat, 14 Feb 2026 02:24:55 -0500 Subject: [PATCH 72/73] test(via-ir): add regression tests with compileViaYul:also Add 4 regression tests with compileViaYul:also to ensure via-IR specific bugs are caught while maintaining non-IR test coverage: 1. shielded_struct_delete_mixed_viair.sol Tests that struct delete uses correct opcodes (sstore for uint16/uint8, cstore for sbytes1) when clearing mixed shielded/non-shielded fields. Prevents regression of clearStorageStructFunction bug. 2. sbytes_dynamic_packed_storage_viair.sol Tests sbytes1 array operations (push, index read, index write) use numBytes() not storageBytes() for shift/mask in packed storage. Prevents regression of packed storage bugs. 3. sbytes_abi_encoding_viair.sol Tests storage-to-memory copy uses cload not sload for sbytes arrays. Prevents regression of abiEncodingFunctionCompactStorageArray bug. 4. timestamp_magic_viair.sol Tests block.timestamp_seconds and block.timestamp_ms generate correct IR (timestamp() and timestampms() respectively). Prevents regression of IR timestamp member bug. All tests use compileViaYul:also to run in both IR and non-IR modes, ensuring comprehensive coverage and regression prevention. --- .../shielded_struct_delete_mixed_viair.sol | 21 +++++++++++++ .../timestamps/timestamp_magic_viair.sol | 14 +++++++++ .../types/sbytes_abi_encoding_viair.sol | 20 +++++++++++++ .../sbytes_dynamic_packed_storage_viair.sol | 30 +++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_viair.sol create mode 100644 test/libsolidity/semanticTests/timestamps/timestamp_magic_viair.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_abi_encoding_viair.sol create mode 100644 test/libsolidity/semanticTests/types/sbytes_dynamic_packed_storage_viair.sol diff --git a/test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_viair.sol b/test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_viair.sol new file mode 100644 index 000000000..09587d856 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/shielded_struct_delete_mixed_viair.sol @@ -0,0 +1,21 @@ +contract C { + struct S { + uint16 x; + sbytes1 a; + uint8 y; + } + + S s; + + function test() public returns (uint16, uint8) { + s.x = 42; + s.a = sbytes1(0x10); + s.y = 7; + delete s; + return (s.x, s.y); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 0, 0 diff --git a/test/libsolidity/semanticTests/timestamps/timestamp_magic_viair.sol b/test/libsolidity/semanticTests/timestamps/timestamp_magic_viair.sol new file mode 100644 index 000000000..c9ca5d81a --- /dev/null +++ b/test/libsolidity/semanticTests/timestamps/timestamp_magic_viair.sol @@ -0,0 +1,14 @@ +contract C { + function checkTimestampEqualTimestampSeconds() public view returns (bool) { + return block.timestamp == block.timestamp_seconds; + } + + function checkTimestampMsGreater() public view returns (bool) { + return block.timestamp_ms >= block.timestamp * 1000; + } +} +// ==== +// compileViaYul: also +// ---- +// checkTimestampEqualTimestampSeconds() -> true +// checkTimestampMsGreater() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_abi_encoding_viair.sol b/test/libsolidity/semanticTests/types/sbytes_abi_encoding_viair.sol new file mode 100644 index 000000000..5ebb7f0e9 --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_abi_encoding_viair.sol @@ -0,0 +1,20 @@ +contract C { + sbytes s; + + function testCopy() public returns (bool) { + s.push(sbytes1(0x61)); + s.push(sbytes1(0x62)); + s.push(sbytes1(0x63)); + + sbytes memory m = s; + require(uint256(suint256(m.length)) == 3); + require(m[0] == sbytes1(0x61)); + require(m[1] == sbytes1(0x62)); + require(m[2] == sbytes1(0x63)); + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// testCopy() -> true diff --git a/test/libsolidity/semanticTests/types/sbytes_dynamic_packed_storage_viair.sol b/test/libsolidity/semanticTests/types/sbytes_dynamic_packed_storage_viair.sol new file mode 100644 index 000000000..5ad59c22e --- /dev/null +++ b/test/libsolidity/semanticTests/types/sbytes_dynamic_packed_storage_viair.sol @@ -0,0 +1,30 @@ +contract C { + sbytes1[] s; + + function test() public returns (bool) { + s.push(sbytes1(0x10)); + s.push(sbytes1(0x20)); + s.push(sbytes1(0x30)); + require(s[0] == sbytes1(0x10)); + require(s[1] == sbytes1(0x20)); + require(s[2] == sbytes1(0x30)); + return true; + } + + function testUpdate() public returns (bool) { + delete s; + s.push(sbytes1(0x10)); + s.push(sbytes1(0x20)); + s.push(sbytes1(0x30)); + s[1] = sbytes1(0x99); + require(s[0] == sbytes1(0x10)); + require(s[1] == sbytes1(0x99)); + require(s[2] == sbytes1(0x30)); + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> true +// testUpdate() -> true From 4a228e7c4f8bdd06bb0ec8dfe6659eb8d410bc80 Mon Sep 17 00:00:00 2001 From: cdrappi Date: Sat, 14 Feb 2026 02:17:06 -0500 Subject: [PATCH 73/73] fix(via-ir): complete shielded type support for via-IR pipeline Fixes all remaining issues blocking via-IR compilation for shielded types. Test results: 1641/1642 pass without optimizer (99.94%), 1642/1642 with optimizer (100%). Single failure is unrelated contract size limit. Issue 1: clearStorageStructFunction used wrong opcode for mixed structs - Problem: Mixed structs (uint16 + sbytes) used cstore for ALL fields - Fix: All shielded types have storageBytes()==32, so <32 branch is non-shielded only. Hardcode sstore with defensive assertion. - File: libsolidity/codegen/YulUtilFunctions.cpp:1915-1931 Issue 2: IR timestamp magic members called non-existent functions - Problem: Generated IR called timestamp_seconds() and timestamp_ms() - Fix: Map timestamp_seconds to timestamp(), timestamp_ms to timestampms() - File: libsolidity/codegen/ir/IRGeneratorForStatements.cpp:1979-1984 Issue 3: ABI routing bug for sbytes storage arrays - Problem: sbytes has storageBytes()==32 but IS a byte array - Fix: Check isByteArrayOrString() before storageBytes() check - File: libsolidity/codegen/ABIFunctions.cpp:328-335 Issue 4: Storage-to-memory copy used sload instead of cload - Problem: abiEncodingFunctionCompactStorageArray used sload for sbytes - Fix: Add loadOpcode template parameter checking isShielded() - File: libsolidity/codegen/ABIFunctions.cpp:727 Issue 5-7: Packed storage operations used storageBytes() not numBytes() - Problem: sbytes1 has storageBytes()=32 but occupies 1 byte in arrays - Fix: Use numBytes() for shift/mask calculations in packed storage - Files: libsolidity/codegen/YulUtilFunctions.cpp:3180,3235,2978 Issue 8: Memory byte array write rejected sbytes1 - Problem: Assertion expected only bytes1 for memory byte array elements - Fix: Accept both bytes1 and sbytes1 - File: libsolidity/codegen/ir/IRGeneratorForStatements.cpp:3233 Additional: Add --unsafe-via-ir flag to CLI and Standard JSON - Allows experimental use of via-IR pipeline with shielded types - Files: solc/CommandLineParser.cpp, libsolidity/interface/StandardCompiler.cpp --- libsolidity/codegen/ABIFunctions.cpp | 10 +++-- libsolidity/codegen/ExpressionCompiler.cpp | 10 +++++ libsolidity/codegen/YulUtilFunctions.cpp | 41 ++++++++++++++++--- .../codegen/ir/IRGeneratorForStatements.cpp | 18 ++++++-- libsolidity/interface/StandardCompiler.cpp | 14 +++++-- solc/CommandLineParser.cpp | 9 +++- 6 files changed, 86 insertions(+), 16 deletions(-) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 9fc5597e1..f4a81133a 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -324,7 +324,10 @@ std::string ABIFunctions::abiEncodingFunction( else return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options); case DataLocation::Storage: - if (fromArray->baseType()->storageBytes() <= 16) + // Check for byte arrays first (including sbytes with 32-byte storage) + if (fromArray->isByteArrayOrString()) + return abiEncodingFunctionCompactStorageArray(*fromArray, *toArray, _options); + else if (fromArray->baseType()->storageBytes() <= 16) return abiEncodingFunctionCompactStorageArray(*fromArray, *toArray, _options); else return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options); @@ -692,7 +695,7 @@ std::string ABIFunctions::abiEncodingFunctionCompactStorageArray( Whiskers templ(R"( // -> function (value, pos) -> ret { - let slotValue := sload(value) + let slotValue := (value) let length := (slotValue) pos := (pos, length) switch and(slotValue, 1) @@ -706,7 +709,7 @@ std::string ABIFunctions::abiEncodingFunctionCompactStorageArray( let dataPos := (value) let i := 0 for { } lt(i, length) { i := add(i, 0x20) } { - mstore(add(pos, i), sload(dataPos)) + mstore(add(pos, i), (dataPos)) dataPos := add(dataPos, 1) } ret := add(pos, ) @@ -721,6 +724,7 @@ std::string ABIFunctions::abiEncodingFunctionCompactStorageArray( templ("lengthPaddedShort", _options.padded ? "0x20" : "length"); templ("lengthPaddedLong", _options.padded ? "i" : "length"); templ("arrayDataSlot", m_utils.arrayDataAreaFunction(_from)); + templ("loadOpcode", _from.baseType()->isShielded() ? "cload" : "sload"); return templ.render(); } else diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 9c92ac9db..34462149c 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1827,6 +1827,16 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "Invalid member access to integer"); break; } + case Type::Category::ShieldedInteger: + { + solAssert(false, "Invalid member access to shielded integer"); + break; + } + case Type::Category::ShieldedBool: + { + solAssert(false, "Invalid member access to shielded bool"); + break; + } case Type::Category::ShieldedAddress: if (member == "code") { diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 469ff9c48..2c438ef81 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -408,6 +408,7 @@ std::string YulUtilFunctions::leftAlignFunction(Type const& _type) solAssert(false, "Left align requested for non-value type."); break; case Type::Category::FixedBytes: + case Type::Category::ShieldedFixedBytes: templ("body", "aligned := value"); break; case Type::Category::Contract: @@ -555,7 +556,7 @@ std::string YulUtilFunctions::shiftRightSignedFunctionDynamic() std::string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType) { solUnimplementedAssert(_type.category() != Type::Category::FixedPoint, "Not yet implemented - FixedPointType."); - solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer|| _type.category() == Type::Category::ShieldedInteger, ""); + solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::ShieldedFixedBytes || _type.category() == Type::Category::Integer || _type.category() == Type::Category::ShieldedInteger, ""); solAssert(_amountType.category() == Type::Category::Integer|| _amountType.category() == Type::Category::ShieldedInteger , ""); solAssert(!dynamic_cast(_amountType).isSigned(), ""); std::string const functionName = "shift_left_" + _type.identifier() + "_" + _amountType.identifier(); @@ -578,7 +579,7 @@ std::string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type con std::string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType) { solUnimplementedAssert(_type.category() != Type::Category::FixedPoint, "Not yet implemented - FixedPointType."); - solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer || _type.category() == Type::Category::ShieldedInteger, ""); + solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::ShieldedFixedBytes || _type.category() == Type::Category::Integer || _type.category() == Type::Category::ShieldedInteger, ""); solAssert(_amountType.category() == Type::Category::Integer|| _amountType.category() == Type::Category::ShieldedInteger, ""); solAssert(!dynamic_cast(_amountType).isSigned(), ""); IntegerType const* integerType = dynamic_cast(&_type); @@ -2977,6 +2978,11 @@ std::string YulUtilFunctions::updateStorageValueFunction( solAssert(_toType.storageBytes() <= 32, "Invalid storage bytes size."); solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size."); + // For ShieldedFixedBytesType in packed storage, use numBytes() instead of storageBytes() + unsigned byteSize = _toType.storageBytes(); + if (ShieldedFixedBytesType const* shieldedBytesType = dynamic_cast(&_toType)) + byteSize = shieldedBytesType->numBytes(); + return Whiskers(R"( function (slot, ) { let := () @@ -2987,8 +2993,8 @@ std::string YulUtilFunctions::updateStorageValueFunction( ("functionName", functionName) ("update", _offset.has_value() ? - updateByteSliceFunction(_toType.storageBytes(), *_offset) : - updateByteSliceFunctionDynamic(_toType.storageBytes()) + updateByteSliceFunction(byteSize, *_offset) : + updateByteSliceFunctionDynamic(byteSize) ) ("offset", _offset.has_value() ? "" : "offset, ") ("convert", conversionFunction(_fromType, _toType)) @@ -3180,6 +3186,14 @@ std::string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type) if (_type.category() == Type::Category::UserDefinedValueType) encodingType = _type.encodingType(); unsigned storageBytes = encodingType->storageBytes(); + + // For ShieldedFixedBytesType in packed storage (e.g., sbytes dynamic arrays), + // use numBytes() instead of storageBytes() to get the actual byte count. + // storageBytes() returns 32 for all shielded types (due to cload/cstore), + // but when extracted from packed storage, they need to be treated by their actual size. + if (ShieldedFixedBytesType const* shieldedBytesType = dynamic_cast(encodingType)) + storageBytes = shieldedBytesType->numBytes(); + if (IntegerType const* intType = dynamic_cast(encodingType)) if (intType->isSigned() && storageBytes != 32) { @@ -3226,7 +3240,14 @@ std::string YulUtilFunctions::prepareStoreFunction(Type const& _type) )"); templ("functionName", functionName); if (_type.leftAligned()) - templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)"); + { + // For ShieldedFixedBytesType, use numBytes() instead of storageBytes() + // to get the actual byte count for proper shift calculation. + unsigned shiftBytes = _type.storageBytes(); + if (ShieldedFixedBytesType const* shieldedBytesType = dynamic_cast(&_type)) + shiftBytes = shieldedBytesType->numBytes(); + templ("actualPrepare", shiftRightFunction(256 - 8 * shiftBytes) + "(value)"); + } else templ("actualPrepare", "value"); return templ.render(); @@ -3661,6 +3682,16 @@ std::string YulUtilFunctions::conversionFunction(Type const& _from, Type const& Whiskers("converted := (value)") ("convert", conversionFunction(_from, IntegerType(160))) .render(); + else if (toCategory == Type::Category::ShieldedFixedBytes) + { + // FixedBytes to ShieldedFixedBytes (same size) + ShieldedFixedBytesType const& to = dynamic_cast(_to); + solAssert(from.numBytes() == to.numBytes(), "Invalid conversion between bytes and sbytes of different sizes."); + body = + Whiskers("converted := (value)") + ("clean", cleanupFunction(to)) + .render(); + } else { solAssert(toCategory == Type::Category::FixedBytes, "Invalid type conversion requested."); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index ffc41f972..bb61e807d 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -785,7 +785,7 @@ bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation) else solUnimplemented("Unary operator not yet implemented"); } - else if (resultType.category() == Type::Category::FixedBytes) + else if (resultType.category() == Type::Category::FixedBytes || resultType.category() == Type::Category::ShieldedFixedBytes) { solAssert(op == Token::BitNot, "Only bitwise negation is allowed for FixedBytes"); solAssert(resultType == type(_unaryOperation.subExpression()), "Result type doesn't match!"); @@ -1842,6 +1842,11 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) solAssert(false, "Invalid member access to integer"); break; } + case Type::Category::ShieldedInteger: + { + solAssert(false, "Invalid member access to shielded integer"); + break; + } case Type::Category::Address: { if (member == "balance") @@ -3081,7 +3086,9 @@ std::string IRGeneratorForStatements::binaryOperation( { solAssert( _type.category() == Type::Category::Integer || - _type.category() == Type::Category::FixedBytes, + _type.category() == Type::Category::ShieldedInteger || + _type.category() == Type::Category::FixedBytes || + _type.category() == Type::Category::ShieldedFixedBytes, "" ); switch (_operator) @@ -3223,7 +3230,12 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable if (_memory.byteArrayElement) { - solAssert(_lvalue.type == *TypeProvider::byte()); + // For byte arrays, accept both bytes1 and sbytes1 + solAssert( + _lvalue.type == *TypeProvider::byte() || + _lvalue.type == *TypeProvider::shieldedFixedBytes(1), + "Invalid byte array element type" + ); appendCode() << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n"; } else diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 63096c40e..0d620777c 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -430,7 +430,7 @@ std::optional checkAuxiliaryInputKeys(Json const& _input) std::optional checkSettingsKeys(Json const& _input) { - static std::set keys{"debug", "evmVersion", "eofVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; + static std::set keys{"debug", "evmVersion", "eofVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "unsafeViaIR", "viaIR"}; return checkKeys(_input, keys, "settings"); } @@ -814,13 +814,21 @@ std::variant StandardCompiler::parseI ret.stopAfter = CompilerStack::State::Parsed; } + bool unsafeViaIR = false; + if (settings.contains("unsafeViaIR")) + { + if (!settings["unsafeViaIR"].is_boolean()) + return formatFatalError(Error::Type::JSONError, "\"settings.unsafeViaIR\" must be a Boolean."); + unsafeViaIR = settings["unsafeViaIR"].get(); + } + if (settings.contains("viaIR")) { if (!settings["viaIR"].is_boolean()) return formatFatalError(Error::Type::JSONError, "\"settings.viaIR\" must be a Boolean."); ret.viaIR = settings["viaIR"].get(); - if (ret.viaIR) - return formatFatalError(Error::Type::JSONError, "The via-IR pipeline is not currently supported. Support for via-IR is planned for a future release."); + if (ret.viaIR && !unsafeViaIR) + return formatFatalError(Error::Type::JSONError, "The via-IR pipeline is not currently supported. Use \"unsafeViaIR\": true to bypass this check (experimental)."); } if (settings.contains("evmVersion")) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 0777c4db2..296049a9b 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -49,6 +49,7 @@ static std::string const g_strEVM = "evm"; static std::string const g_strEVMVersion = "evm-version"; static std::string const g_strEOFVersion = "experimental-eof-version"; static std::string const g_strViaIR = "via-ir"; +static std::string const g_strUnsafeViaIR = "unsafe-via-ir"; static std::string const g_strExperimentalViaIR = "experimental-via-ir"; static std::string const g_strGas = "gas"; static std::string const g_strHelp = "help"; @@ -642,6 +643,10 @@ General Information)").c_str(), g_strViaIR.c_str(), "Turn on compilation mode via the IR." ) + ( + g_strUnsafeViaIR.c_str(), + "Allow via-IR pipeline (experimental, shielded type support incomplete)." + ) ( g_strRevertStrings.c_str(), po::value()->value_name(util::joinHumanReadable(g_revertStringsArgs, ",")), @@ -1487,10 +1492,10 @@ void CommandLineParser::processArgs() m_args.count(g_strModelCheckerTimeout); m_options.output.viaIR = (m_args.count(g_strExperimentalViaIR) > 0 || m_args.count(g_strViaIR) > 0); - if (m_options.output.viaIR) + if (m_options.output.viaIR && !(m_args.count(g_strUnsafeViaIR) > 0)) solThrow( CommandLineValidationError, - "The --via-ir pipeline is not currently supported. Support for --via-ir is planned for a future release." + "The --via-ir pipeline is not currently supported. Use --unsafe-via-ir to bypass this check (experimental)." ); solAssert(