fix(function_ref): do not mark an inline-stored callable as borrowed - #13
Merged
Merged
Conversation
A callable that is trivially copyable and fits the data word is COPIED into the
ref by `make_c_callback` — the ref then owns everything it needs and points at
nothing. The single converting constructor nevertheless carried
`[[clang::lifetimebound]]`, so building a ref from a temporary of that kind was
diagnosed as a dangling reference and, under `-Werror`, rejected outright:
return function_ref<void( int )>{ [ p ]( int const v ) { *p = v; } };
error: returning address of local temporary object [-Wreturn-stack-address]
The attribute cannot be applied conditionally, so split the constructor in two
and let the constraints partition on whether the target is stored inline:
- not stored inline -> the ref points at the caller's object, which must
outlive it. Keeps `lifetimebound`, unchanged behaviour.
- stored inline -> copied, borrows nothing, so a temporary is fine.
The classification is exposed as `stored_inline<F>` so callers can assert the
property they are relying on. Note it is about storage, not value category: an
lvalue of an inline-storable type was never borrowed either.
Tests: a ref built from a temporary that captures one pointer stays valid after
the full-expression and is returnable; a two-pointer capture is still classified
as borrowing (and still diagnosed as such by the compiler when returned).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
function_ref's converting constructor is annotated[[clang::lifetimebound]],which is right for the general case: the ref points at the caller's object and
that object has to outlive it.
But
make_c_callbackdoes not always point. A callable that is triviallycopyable and fits in the data word is copied into the ref, which then owns
everything it needs and refers to nothing. For those, the annotation describes a
borrow that does not exist — and clang enforces it:
Under
-Werrorthat is a hard rejection of a perfectly sound use. Theworkarounds are all worse than the thing they avoid: hoist the callable into a
named object whose type you often cannot spell (it may come from a
type-dispatched
if constexprbranch), give it static storage and lose theper-instance data, or silence the diagnostic and lose it everywhere.
Fix
[[clang::lifetimebound]]cannot be applied conditionally, so split theconstructor in two and let the constraints partition on whether the target is
stored inline:
lifetimebound; behaviour unchanged.safe and must not be diagnosed.
The predicate is exposed as
stored_inline<F>so callers canstatic_assertthe property they rely on rather than assuming it.
Note the split is on storage, not value category: an lvalue of an
inline-storable type was never borrowed either, so both overloads accept both
value categories and only the borrowing one carries the annotation.
Tests
Added to
test/function_ref_test.cpp:full-expression and can be returned;
stored_inlineclassifies a one-pointer capture and a plain function pointeras copied, and a two-pointer capture as borrowing;
Verified separately that a two-pointer capture returned by value is still
diagnosed by
-Wreturn-stack-address— the safety property is preserved exactlywhere it applies. Full suite: 6/6 green.