Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix internal error when using an empty tuple with a ternary operator #15025

Merged
merged 1 commit into from Apr 18, 2024

Conversation

pgebal
Copy link
Collaborator

@pgebal pgebal commented Apr 12, 2024

Closes #14929

The reason for this bug is that we forgot to handle an empty tuple in a code that translates instances our internal classes to z3 expressions.
I fixed it by adding the missing part of an if statement in Z3Interface::toZ3Expr.

@pgebal pgebal requested a review from blishko April 12, 2024 16:30
@pgebal pgebal self-assigned this Apr 12, 2024
@pgebal pgebal force-pushed the fix_trenary_operator_with_tuples branch from 77def7c to fe4c531 Compare April 12, 2024 16:32
@@ -0,0 +1,9 @@
contract C {
function f() public pure {
true ? () : (); // bug fix test, proper handling of empty tuples
Copy link
Member

@cameel cameel Apr 12, 2024

Choose a reason for hiding this comment

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

Note that many things that are not supposed to return anything actually return an empty tuple. It would be best to add a few more tests to make sure they all work.

For example using a function with no return value causes this ICE too:

function f() {}
function g() {}

contract C {
    function test() public {
        true ? f() : g();
    }
}

Another case are events and errors (both instances and types), though I can't think of a way those could be used in a ternary expression.

An attempt to return an empty tuple from a function triggers the ICE too:

contract C {
    function test() public {
        return true ? () : ();
    }
}

Copy link
Collaborator Author

@pgebal pgebal Apr 16, 2024

Choose a reason for hiding this comment

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

Thanks for test ideas. Those 2 cases no longer cause ICE after the fix.
Where can I learn more about what returns an empty tuple? Is istsomewhere in solidity docs?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think it's explicitly documented unfortunately. The way to find out is to basically try things out and look at error messages. I know it from writing lots of test cases (and sometimes from implementation).

In any case, the ones I already listed probably cover most of it. Aside from errors and events most of the cases will just be various kinds of functions that don't return anything, including the built-in ones like require() or <address>.transfer(). Maybe some of the syntax elements and control structures that can be used inside expressions also technically return tuples, but you'd have to try them one by one. If you added the ones I listed, it's good enough.

libsmtutil/Z3Interface.cpp Outdated Show resolved Hide resolved
Comment on lines 172 to 177
else if (n == "tuple_constructor")
{
auto constructor = z3::func_decl(m_context, Z3_get_tuple_sort_mk_decl(m_context, z3Sort(*_expr.sort)));
smtAssert(constructor.arity() == arguments.size(), "");
return constructor();
}
Copy link
Member

@cameel cameel Apr 12, 2024

Choose a reason for hiding this comment

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

The fix looks quite generic. Why were only empty tuples affected?

I checked a simple case like this and it seems to work fine, but is it possible that things were actually broken in more complex cases of non-empty tuples?

contract C {
    function test() public {
        true ? (1, 2) : (3, 4);
    }
}

Copy link
Collaborator Author

@pgebal pgebal Apr 16, 2024

Choose a reason for hiding this comment

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

That's unlikely. The reason for this bug is that we forgot to handle an empty tuple in a code that translates our internal classes to z3 expression. Non-empty tuples are handled correctly.

Copy link
Member

Choose a reason for hiding this comment

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

So are empty tuples encoded differently than non-empty ones?

Copy link
Contributor

Choose a reason for hiding this comment

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

No, the representation is the same. The problem is that before we get to handling tuples (tuple_constructor) there is this check:

else if (arguments.empty())

So there is a special case for handling expressions with zero arguments, but it did not cover empty tuples before.

Changelog.md Outdated Show resolved Hide resolved
@blishko
Copy link
Contributor

blishko commented Apr 15, 2024

@pgebal: Related to @cameel 's comments, please add explanation what was causing the bug and how is the change fixing it.

@pgebal pgebal force-pushed the fix_trenary_operator_with_tuples branch from e7cc066 to 22ff73e Compare April 16, 2024 17:01
@pgebal
Copy link
Collaborator Author

pgebal commented Apr 16, 2024

@pgebal: Related to @cameel 's comments, please add explanation what was causing the bug and how is the change fixing it.

done

@pgebal pgebal requested a review from cameel April 16, 2024 17:22
Copy link
Contributor

@blishko blishko left a comment

Choose a reason for hiding this comment

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

LGTM!

There is a general code to handle tuple constructor, but this shortcut to handle expressions without arguments is taken for empty tuple before we get to the general handler. So the bug happens only for empty tuples and this is a good place to fix it.

@blishko
Copy link
Contributor

blishko commented Apr 16, 2024

@pgebal, can you rebase the PR? Then we can merge.

@pgebal pgebal force-pushed the fix_trenary_operator_with_tuples branch from 22ff73e to d865aba Compare April 16, 2024 17:30
@pgebal
Copy link
Collaborator Author

pgebal commented Apr 16, 2024

@pgebal, can you rebase the PR? Then we can merge.

sure, done

@cameel
Copy link
Member

cameel commented Apr 17, 2024

Needs a rebase on develop now that we merged the fix for failing tests (#15035).

@pgebal pgebal force-pushed the fix_trenary_operator_with_tuples branch from d865aba to 33f0a5f Compare April 18, 2024 15:46
@pgebal pgebal merged commit 00b2e54 into ethereum:develop Apr 18, 2024
73 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SMT logic error for a conditional with empty tuple types
3 participants