Skip to content

Make ConstantEvaluator truncate the result of shift operations#16597

Open
matheusaaguiar wants to merge 5 commits intodevelopfrom
ConstantEvaluator-truncate-shiftL-value
Open

Make ConstantEvaluator truncate the result of shift operations#16597
matheusaaguiar wants to merge 5 commits intodevelopfrom
ConstantEvaluator-truncate-shiftL-value

Conversation

@matheusaaguiar
Copy link
Copy Markdown
Contributor

Fix #16596.
Spotted in #16456 (comment).

))
{
TypedValue convertedValue = convertType(*value, *resultType);
TypedValue convertedValue = TokenTraits::isShiftOp(_operation.getOperator()) ?
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it could be done only for left shift.

@matheusaaguiar
Copy link
Copy Markdown
Contributor Author

Not sure about a Changelog entry...

@matheusaaguiar matheusaaguiar force-pushed the ConstantEvaluator-truncate-shiftL-value branch from 86ac941 to a2cb1da Compare April 21, 2026 07:11
Copy link
Copy Markdown
Member

@clonker clonker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some code structure feedback and questions. Overall looks correct to me. I think BitNot has similar issues. For example ~uint8(0) and ~uint8(-128) both fail constant eval. If you agree, this could be folded into this pr or be a follow up.

Not sure about a Changelog entry...

It's user-facing, isn't it? So imo having an entry is the right move.

solAssert(integerType);

solAssert(_value.denominator() == 1);
bigint integerValue = _value.numerator() / _value.denominator();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bigint integerValue = _value.numerator() / _value.denominator();
bigint integerValue = _value.numerator();

you assert that denom is 1 right above

auto const* integerType = dynamic_cast<IntegerType const*>(&_type);
solAssert(integerType);

solAssert(_value.denominator() == 1);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why can we assume that, because it's an IntegerType? Is rational always in reduced/normalized form?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's IntegerType. IIRC, in comptime, it is always normalized. I will confirm this.

integerValue = integerValue & mask;

// extend sign if needed
if (integerType->isSigned() && (integerValue & sign))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (integerType->isSigned() && (integerValue & sign))
bool const isNegative = integerType->isSigned() && boost::multiprecision::bit_test(integerValue, numBits - 1);
if (isNegative)

then the sign mask above isn't needed anymore

Comment on lines +438 to +440
TypedValue convertedValue = TokenTraits::isShiftOp(_operation.getOperator()) ?
cleanupValue(*value, *resultType) :
convertType(*value, *resultType);
Copy link
Copy Markdown
Member

@clonker clonker Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I'd rather introduce a helper rational truncateToType(rational const& _value, IntegerType const& _type) and then do the conversion directly here. that would avoid mixing concerns where, right now, cleanupValue both converts and truncates. I imagine it could read something like

Suggested change
TypedValue convertedValue = TokenTraits::isShiftOp(_operation.getOperator()) ?
cleanupValue(*value, *resultType) :
convertType(*value, *resultType);
rational resultValue = *value;
if (isShiftOp)
if (auto const* integerType = dynamic_cast...)
resultValue = truncateToType(resultValue, *integerType);
TypedValue convertedValue = convertType(resultValue, *resultType);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, was concerned too about cleanupValue calling convertType at the end...will implement your suggestion.

@matheusaaguiar matheusaaguiar force-pushed the ConstantEvaluator-truncate-shiftL-value branch from 2963520 to f695046 Compare April 23, 2026 01:16
@matheusaaguiar matheusaaguiar force-pushed the ConstantEvaluator-truncate-shiftL-value branch from f695046 to 36f5fa3 Compare April 23, 2026 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ConstantEvaluator does not truncate the result of left shift operations

2 participants