Handle comparisons against translated functions#3
Handle comparisons against translated functions#3nunoplopes merged 59 commits intoCpp2Rust:masterfrom
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
Non-capturing lambdas are translated as Option<fn>, same as free functions, because they can be decayed to free functions. Capturing lambdas remain Rc<dyn Fn>.
Previously function pointers were modeled as Option<fn>, but this is
problematic for function pointer casts. Option<fn> works well in unsafe
with std::mem::transmute, however there is no safe way to achieve the
same operation in refcount.
This is solved using the new Ptr<fn>. To allow casting between different
function types, use type erased Rc<dyn Any> inside the new PtrKind::Fn.
Equality of function pointers is achieved through implementing the
OriginalAlloc::address method.
The C standard allows converting function pointers between incompatible
function types. UB is triggered only when the incompatible pointer is
called. For this reason the new FnState implements 2 new concepts:
1. casting adaptors (to allow argument casting between ABI compatible
types)
2. provenance stack (to allow round-trip function pointer casts)
For 1., consider the following cast:
int fn_taking_int_ptr(int *p);
int (*fn_taking_void_ptr)(void*) = (int (*)(void*))fn_taking_int_ptr;
Calling fn_taking_int_ptr with an int* argument works because both int*
and void* have the same size. To support this in Rust we need to create
an int* -> void* adapter when casting from fn_taking_int_ptr to
fn_taking_void_ptr:
fn_taking_int_ptr.cast_fn::<fn(AnyPtr) -> i32>(Some(
(|a0: AnyPtr| -> i32 {
fn_taking_int_ptr(a0.cast::<i32>().unwrap())
}) as fn(AnyPtr) -> i32
))
The job of the adapter is to convert from AnyPtr to Ptr<i32>.
Ptr::cast_fn is a new function that takes as type argument the type of
the target function pointer and an optional adaptor. If cast_fn receives
None, then there is no valid adaptor from source to target, matching the
UB semantics of calling a function through an incompatible function
pointer:
int add(int a, int b) { return a + b; }
void (*wrong)(void) = (void (*)(void))add;
wrong()
For 2., the provenance stack contains all casts performed on the pointer
in the past. Compared to PtrKind::Reinterpreted, PtrKind::Fn has no
backing byte storage through OriginalAlloc, so each cast must know its
history in order to allow round-trip casts, such as:
int (*)(int, int) -> void (*)(void) -> int (*)(int, int)
(1) (2)
For this specific case, where both (1) and (2) create non-compatible
adaptors (because of non-compatible arguments), we cannot recover a call
to the original function after (1) is performed. For this to work, save
a stack of provenance, and when (2) is perfomed, cast_fn recovers the
original function pointer. See test_roundtrip in fn_ptr_cast.cpp.
A current limitation of this approach is that it only allows function
pointer casts where the source is a direct declaration of a function.
Accessing a function pointer through a member field for example, would
create a capturing adapter which does not coerce in a fn inside Ptr<fn>.
Also make the rules public so that generated code can reference them.
|
@nunoplopes this is ready for review |
| } | ||
|
|
||
| fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { | ||
| pub fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { |
There was a problem hiding this comment.
To make f5 visible outside the crate so that translted code can take the address of this function: rules::stdio_tgt_refcount::f5
There was a problem hiding this comment.
That's a major hack. No way this works. Rule's names are internal and should not be exported.
Translated programs only link with libcc2rs. Add new symbols there.
There was a problem hiding this comment.
So generated code should use libcc2rs::fread instead of rules::stdio_tgt_refcount::f5? In this case libcc2rs::fread should be defined as a call to rules::stdio_tgt_refcount::f5. For this to work, I think making f5 public is still required
There was a problem hiding this comment.
No, we should not export anything in rules. And should definitely not export a function named f5.
You need to create an fread function in libcc2rs.
There was a problem hiding this comment.
We also need to support calling into mapped functions using function pointers, like:
auto fn = &fread;
fn(...);This means that the body of libcc2rs::fread and rules::stdio_tgt_refcount::f5 has to be exactly the same. Without exporting f5 from rules, we will copy-paste the body of rules::stdio_tgt_refcount::f5 inside libcc2rs::fread
There was a problem hiding this comment.
That's ok for now. But we are not exporting the rules directory and f5 names.
There was a problem hiding this comment.
I created fread_refcount and fread_unsafe. Generated code can use these functions for saving the address of fread. Generated code can also call into fread through a function pointer.
To avoid code duplication, the fread translation rules also call into fread_refcount and fread_unsafe
| static void Trim(std::string &s) { | ||
| auto is_space = [](unsigned char c) { return std::isspace(c); }; | ||
| s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), is_space)); | ||
| s.erase(std::find_if_not(s.rbegin(), s.rend(), is_space).base(), s.end()); |
There was a problem hiding this comment.
instead of modifying the string, it's cheaper to do substr on a string_view
There was a problem hiding this comment.
I moved to string_view
|
|
||
| void Unwrap(std::string &s, std::string_view prefix, std::string_view suffix) { | ||
| Trim(s); | ||
| if (s.size() >= prefix.size() + suffix.size() && s.starts_with(prefix) && |
There was a problem hiding this comment.
this size comparison is not needed, unless prefix and suffix might be the same?
There was a problem hiding this comment.
I moved this check in an assert so that it only triggers in Debug builds
This PR adds support for translating comparisons against functions that have translation rules
fn_ptr == translated_rule, for example infn1 == freadin fn_ptr_stdlib_compare.cpp: