Skip to content

Commit 3abf272

Browse files
Ruko97chriseth
authored andcommitted
changed output of formatNumberReadable so that it shows powers of two and one-less-than powers of two in a more compact format
1 parent 310a58d commit 3abf272

13 files changed

+252
-137
lines changed

Changelog.md

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Compiler Features:
1616
Bugfixes:
1717
* Yul Optimizer: Hash hex and decimal literals according to their value instead of their representation, improving the detection of equivalent functions.
1818
* Solidity Upgrade Tool ``solidity-upgrade``: Fix the tool returning success code on uncaught exceptions.
19+
* SMTChecker: Fix display error for negative integers that are one more than powers of two.
20+
* SMTChecker: Improved readability for large integers that are powers of two or almost powers of two in error messages.
1921

2022

2123
### 0.8.17 (2022-09-08)

libsolutil/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ set(sources
4343
)
4444

4545
add_library(solutil ${sources})
46-
target_link_libraries(solutil PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::system range-v3)
46+
target_link_libraries(solutil PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::system range-v3 fmt::fmt-header-only)
4747
target_include_directories(solutil PUBLIC "${CMAKE_SOURCE_DIR}")
4848
add_dependencies(solutil solidity_BuildInfo.h)
4949

libsolutil/StringUtils.cpp

+78
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <vector>
2828

2929
using namespace std;
30+
using namespace solidity;
3031
using namespace solidity::util;
3132

3233
bool solidity::util::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance, size_t _lenThreshold)
@@ -113,3 +114,80 @@ string solidity::util::suffixedVariableNameList(string const& _baseName, size_t
113114
}
114115
return result;
115116
}
117+
118+
namespace
119+
{
120+
121+
/// Try to format as N * 2**x
122+
optional<string> tryFormatPowerOfTwo(bigint const& _value)
123+
{
124+
bigint prefix = _value;
125+
126+
// when multiple trailing zero bytes, format as N * 2**x
127+
int i = 0;
128+
for (; (prefix & 0xff) == 0; prefix >>= 8)
129+
++i;
130+
if (i <= 2)
131+
return nullopt;
132+
133+
// 0x100 yields 2**8 (N is 1 and redundant)
134+
if (prefix == 1)
135+
return {fmt::format("2**{}", i * 8)};
136+
else if ((prefix & (prefix - 1)) == 0)
137+
{
138+
int j = 0;
139+
for (; (prefix & 0x1) == 0; prefix >>= 1)
140+
j++;
141+
return {fmt::format("2**{}", i * 8 + j)};
142+
}
143+
else
144+
return {fmt::format(
145+
"{} * 2**{}",
146+
toHex(toCompactBigEndian(prefix), HexPrefix::Add, HexCase::Mixed),
147+
i * 8
148+
)};
149+
}
150+
151+
}
152+
153+
string solidity::util::formatNumberReadable(bigint const& _value, bool _useTruncation)
154+
{
155+
bool const isNegative = _value < 0;
156+
bigint const absValue = isNegative ? (bigint(-1) * _value) : bigint(_value);
157+
string const sign = isNegative ? "-" : "";
158+
159+
// smaller numbers return as decimal
160+
if (absValue <= 0x1000000)
161+
return sign + absValue.str();
162+
163+
if (auto result = tryFormatPowerOfTwo(absValue))
164+
return {sign + *result};
165+
else if (auto result = tryFormatPowerOfTwo(absValue + 1))
166+
return {sign + *result + (isNegative ? " + 1" : " - 1")};
167+
168+
string str = toHex(toCompactBigEndian(absValue), HexPrefix::Add, HexCase::Mixed);
169+
170+
if (_useTruncation)
171+
{
172+
// return as interior-truncated hex.
173+
size_t len = str.size();
174+
175+
if (len < 24)
176+
return sign + str;
177+
178+
size_t const initialChars = 6;
179+
size_t const finalChars = 4;
180+
size_t numSkipped = len - initialChars - finalChars;
181+
182+
return fmt::format(
183+
"{}{}...{{+{} more}}...{}",
184+
sign,
185+
str.substr(0, initialChars),
186+
numSkipped,
187+
str.substr(len-finalChars, len)
188+
);
189+
}
190+
191+
return sign + str;
192+
}
193+

libsolutil/StringUtils.h

+6-97
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include <libsolutil/CommonData.h>
2828
#include <libsolutil/Numeric.h>
2929

30+
#include <fmt/format.h>
31+
3032
#include <algorithm>
3133
#include <limits>
3234
#include <locale>
@@ -101,110 +103,17 @@ std::string joinHumanReadablePrefixed
101103
return _separator + joinHumanReadable(_list, _separator, _lastSeparator);
102104
}
103105

104-
/// Same as @ref formatNumberReadable but only for unsigned numbers
105-
template <class T>
106-
inline std::string formatUnsignedNumberReadable (
107-
T const& _value,
108-
bool _useTruncation = false
109-
)
110-
{
111-
static_assert(
112-
std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed,
113-
"only unsigned types or bigint supported"
114-
); //bigint does not carry sign bit on shift
115-
116-
// smaller numbers return as decimal
117-
if (_value <= 0x1000000)
118-
return _value.str();
119-
120-
HexCase hexcase = HexCase::Mixed;
121-
HexPrefix prefix = HexPrefix::Add;
122-
123-
// when multiple trailing zero bytes, format as N * 2**x
124-
int i = 0;
125-
T v = _value;
126-
for (; (v & 0xff) == 0; v >>= 8)
127-
++i;
128-
if (i > 2)
129-
{
130-
// 0x100 yields 2**8 (N is 1 and redundant)
131-
if (v == 1)
132-
return "2**" + std::to_string(i * 8);
133-
return toHex(toCompactBigEndian(v), prefix, hexcase) +
134-
" * 2**" +
135-
std::to_string(i * 8);
136-
}
137-
138-
// when multiple trailing FF bytes, format as N * 2**x - 1
139-
i = 0;
140-
for (v = _value; (v & 0xff) == 0xff; v >>= 8)
141-
++i;
142-
if (i > 2)
143-
{
144-
// 0xFF yields 2**8 - 1 (v is 0 in that case)
145-
if (v == 0)
146-
return "2**" + std::to_string(i * 8) + " - 1";
147-
return toHex(toCompactBigEndian(T(v + 1)), prefix, hexcase) +
148-
" * 2**" + std::to_string(i * 8) +
149-
" - 1";
150-
}
151-
152-
std::string str = toHex(toCompactBigEndian(_value), prefix, hexcase);
153-
if (_useTruncation)
154-
{
155-
// return as interior-truncated hex.
156-
size_t len = str.size();
157-
158-
if (len < 24)
159-
return str;
160-
161-
size_t const initialChars = (prefix == HexPrefix::Add) ? 6 : 4;
162-
size_t const finalChars = 4;
163-
size_t numSkipped = len - initialChars - finalChars;
164-
165-
return str.substr(0, initialChars) +
166-
"...{+" +
167-
std::to_string(numSkipped) +
168-
" more}..." +
169-
str.substr(len-finalChars, len);
170-
}
171-
172-
// otherwise, show whole value.
173-
return str;
174-
}
175-
176106
/// Formats large numbers to be easily readable by humans.
177107
/// Returns decimal representation for smaller numbers; hex for large numbers.
178108
/// "Special" numbers, powers-of-two and powers-of-two minus 1, are returned in
179109
/// formulaic form like 0x01 * 2**24 - 1.
180-
/// @a T can be any integer variable, will typically be u160, u256 or bigint.
110+
/// @a T can be any integer type, will typically be u160, u256 or bigint.
181111
/// @param _value to be formatted
182112
/// @param _useTruncation if true, internal truncation is also applied,
183113
/// like 0x5555...{+56 more}...5555
184-
/// @example formatNumberReadable((u256)0x7ffffff) = "0x08 * 2**24"
185-
/// @example formatNumberReadable(-57896044618658097711785492504343953926634992332820282019728792003956564819968) = -0x80 * 2**248
186-
template <class T>
187-
inline std::string formatNumberReadable(
188-
T const& _value,
189-
bool _useTruncation = false
190-
)
191-
{
192-
static_assert(
193-
std::numeric_limits<T>::is_integer,
194-
"only integer numbers are supported"
195-
);
196-
197-
if (_value >= 0)
198-
{
199-
bigint const _v = bigint(_value);
200-
return formatUnsignedNumberReadable(_v, _useTruncation);
201-
}
202-
else
203-
{
204-
bigint const _abs_value = bigint(-1) * _value;
205-
return "-" + formatUnsignedNumberReadable(_abs_value, _useTruncation);
206-
}
207-
}
114+
/// @example formatNumberReadable((u256)0x7ffffff) = "2**27 - 1"
115+
/// @example formatNumberReadable(-57896044618658097711785492504343953926634992332820282019728792003956564819968) = -2**255
116+
std::string formatNumberReadable(bigint const& _value, bool _useTruncation = false);
208117

209118
/// Safely converts an unsigned integer as string into an unsigned int type.
210119
///

test/libsolidity/smtCheckerTests/operators/division_3.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ contract C {
77
// ====
88
// SMTEngine: all
99
// ----
10-
// Warning 4984: (95-100): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\ny = (- 1)\n = 0\n\nTransaction trace:\nC.constructor()\nC.f((- 57896044618658097711785492504343953926634992332820282019728792003956564819968), (- 1))
10+
// Warning 4984: (95-100): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.\nCounterexample:\n\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\ny = (- 1)\n = 0\n\nTransaction trace:\nC.constructor()\nC.f((- 57896044618658097711785492504343953926634992332820282019728792003956564819968), (- 1))

test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ contract C {
77
// SMTEngine: all
88
// SMTIgnoreCex: yes
99
// ----
10-
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
10+
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.
1111
// Warning 4281: (77-82): CHC: Division by zero happens here.

test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ contract C {
88
// SMTEngine: all
99
// SMTIgnoreOS: macos
1010
// ----
11-
// Warning 4984: (96-101): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = 57896044618658097711785492504343953926634992332820282019728792003956564819967\ny = (- 1)\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(57896044618658097711785492504343953926634992332820282019728792003956564819967, (- 1))
11+
// Warning 4984: (96-101): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.\nCounterexample:\n\nx = 57896044618658097711785492504343953926634992332820282019728792003956564819967\ny = (- 1)\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(57896044618658097711785492504343953926634992332820282019728792003956564819967, (- 1))

test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ contract C {
88
// SMTEngine: all
99
// SMTIgnoreCex: yes
1010
// ----
11-
// Warning 3944: (78-83): CHC: Underflow (resulting value less than -0x80 * 2**248) happens here.
12-
// Warning 4984: (78-83): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
11+
// Warning 3944: (78-83): CHC: Underflow (resulting value less than -2**255) happens here.
12+
// Warning 4984: (78-83): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.

test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ contract C {
77
// SMTEngine: all
88
// SMTIgnoreCex: yes
99
// ----
10-
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -0x80 * 2**248) happens here.
11-
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
10+
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -2**255) happens here.
11+
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.

test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ contract C {
77
// SMTEngine: all
88
// SMTIgnoreCex: yes
99
// ----
10-
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -0x80 * 2**248) happens here.
11-
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
10+
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -2**255) happens here.
11+
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.

test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ contract C {
77
// SMTEngine: all
88
// SMTIgnoreCex: yes
99
// ----
10-
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -0x80 * 2**248) happens here.
11-
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
10+
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -2**255) happens here.
11+
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.

test/libsolidity/smtCheckerTests/unchecked/signed_mod.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ contract C {
1515
// SMTEngine: all
1616
// ----
1717
// Warning 4281: (85-90): CHC: Division by zero happens here.\nCounterexample:\n\na = 0\nb = 0\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(0, 0)
18-
// Warning 4984: (242-248): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\n_check = true\n = 0\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\n\nTransaction trace:\nC.constructor()\nC.g(true)
18+
// Warning 4984: (242-248): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.\nCounterexample:\n\n_check = true\n = 0\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\n\nTransaction trace:\nC.constructor()\nC.g(true)

0 commit comments

Comments
 (0)