From 7468a6a40422a265c19c09273dd2146e70bdfe50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 11 Mar 2026 08:28:40 +0100 Subject: [PATCH] Fix ICE for user-defined value type in external library function UserDefinedValueType::signatureInExternalFunction() had an unconditional solAssert(false), assuming the type would always be unwrapped before signature generation. This fails for library external functions with storage array/mapping parameters of UDVT, since ArrayType::interfaceType(inLibrary=true) returns the storage array without unwrapping the base type. Delegate to underlyingType().signatureInExternalFunction() instead, consistent with how encodingType() and interfaceType() already behave. Fixes #16225 --- Changelog.md | 1 + libsolidity/ast/Types.h | 5 +- .../storage_array_library.sol | 70 +++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/semanticTests/userDefinedValueType/storage_array_library.sol diff --git a/Changelog.md b/Changelog.md index ec45b99c75d3..333a42ca3220 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Compiler Features: * Yul EVM Code Transform: Improve stack shuffler performance by fixing a BFS deduplication issue. Bugfixes: +* Type System: Fix internal compiler error when using user-defined value types in storage arrays or mappings in external library functions. ### 0.8.34 (2026-02-18) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index cddedc0f8c8f..b7d3a24ab59a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1196,7 +1196,10 @@ class UserDefinedValueType: public Type std::string toString(bool _withoutDataLocation) const override; std::string canonicalName() const override; - std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); } + std::string signatureInExternalFunction(bool _structsByName) const override + { + return underlyingType().signatureInExternalFunction(_structsByName); + } protected: std::vector> makeStackItems() const override; diff --git a/test/libsolidity/semanticTests/userDefinedValueType/storage_array_library.sol b/test/libsolidity/semanticTests/userDefinedValueType/storage_array_library.sol new file mode 100644 index 000000000000..6e172b948f64 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/storage_array_library.sol @@ -0,0 +1,70 @@ +// Used to cause ICE in signatureInExternalFunction. +type MyUint is uint; +type MyAddr is address; +type MyBytes1 is bytes1; + +struct S { MyUint x; } + +library L { + function getArray(MyUint[] storage _a, uint _i) external view returns (MyUint) { + return _a[_i]; + } + function getNestedArray(MyUint[][] storage _a, uint _i, uint _j) external view returns (MyUint) { + return _a[_i][_j]; + } + function getMappingByValue(mapping(uint => MyAddr) storage _m, uint _k) external view returns (MyAddr) { + return _m[_k]; + } + function getMappingByKey(mapping(MyUint => uint) storage _m, MyUint _k) external view returns (uint) { + return _m[_k]; + } + function getStruct(S[] storage _s, uint _i) external view returns (MyUint) { + return _s[_i].x; + } + function getBytes1Array(MyBytes1[] storage _a, uint _i) external view returns (MyBytes1) { + return _a[_i]; + } +} + +contract C { + MyUint[] uintArr; + MyUint[][] nestedArr; + mapping(uint => MyAddr) addrMap; + mapping(MyUint => uint) keyMap; + S[] structArr; + MyBytes1[] bytes1Arr; + + function testArray() public returns (uint) { + uintArr.push(MyUint.wrap(42)); + return MyUint.unwrap(L.getArray(uintArr, 0)); + } + function testNestedArray() public returns (uint) { + nestedArr.push(); + nestedArr[0].push(MyUint.wrap(7)); + return MyUint.unwrap(L.getNestedArray(nestedArr, 0, 0)); + } + function testMappingByValue() public returns (address) { + addrMap[1] = MyAddr.wrap(address(0xBEEF)); + return MyAddr.unwrap(L.getMappingByValue(addrMap, 1)); + } + function testMappingByKey() public returns (uint) { + keyMap[MyUint.wrap(5)] = 123; + return L.getMappingByKey(keyMap, MyUint.wrap(5)); + } + function testStruct() public returns (uint) { + structArr.push(S(MyUint.wrap(99))); + return MyUint.unwrap(L.getStruct(structArr, 0)); + } + function testBytes1Array() public returns (bytes1) { + bytes1Arr.push(MyBytes1.wrap(0xab)); + return MyBytes1.unwrap(L.getBytes1Array(bytes1Arr, 0)); + } +} +// ---- +// library: L +// testArray() -> 42 +// testNestedArray() -> 7 +// testMappingByValue() -> 0xbeef +// testMappingByKey() -> 123 +// testStruct() -> 99 +// testBytes1Array() -> 0xab00000000000000000000000000000000000000000000000000000000000000