Prevent GMP values in lambdas from outliving their stack frame #168
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
So I recently noticed that while p4c builds on macOS, the tests fail. I investigated a little and tracked down the issue. Here's what's happening:
In constantFolding.cpp, the binary operators are handling by a common method, DoConstantFolding::binary(), which takes a std::function as an argument. The std::function accepts two GMP values (i.e., mpz_class values) and performs the appropriate binary operation, returning a new value. Here's an example:
The problem here is that the return type of these lambdas are inferred, and the inferred type is not mpz_class. For this example, the inferred return type is actually:
What's happening here is that operations on mpz_class values build up an expression tree which is then evaluated when it's assigned to another mpz_class value. This didn't get noticed because while binary() accepts a
std::function<mpz_class(mpz_class, mpz_class)>
, std::function will happily store a lambda whose return type is implicitly convertible to an mpz_class, which is the case for __gmp_expr.So, why is this bad? Well, take a look at this code snippet from gmpxx.h:
As this code implies, __gmp_resolve_ref::ref_type is a reference. The lambda is returning a __gmp_expr that contains references to values which are on its stack frame. This is obviously ill-advised. =)
This works in GCC, presumably, because the code it generates doesn't clobber those values before the __gmp_expr is evaluated. Clang users are not so lucky, which means that DoConstantFolding::binary() ends up generating garbage values, causing tests to fail.
This patch adds explicit return types to all the lambdas in constantFolding.cpp that have this issue. This fixes the problem, since the implicit conversion happens before the stack frame gets popped, and with this patch applied all tests pass on macOS.