-
-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
std::tuple dangling reference - implicit conversion #2226
Comments
I have no idea how to fix this. |
I have a few solution ideas.
What do you think @nlohmann ? |
I can create a pull request for one of from last 3 my ideas, but I need a decision, or an another acceptable solution @nlohmann . |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
bad bot! does not solve this problem, because it isn't about FROM json conversion. |
PRs welcome! |
but which solution? |
(Sorry, I'll have a look) |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
@schaumb, perhaps your initial example leaves original intent not entirely clear. An usable
Tuples containing const references are somewhat dangerous, as it's easy to make these references dangling. Just like
On the other hand, I tried to call
It works as expected (see here). |
My example was a way dummier than the original issue in our production code, because I simplified to the cause of the bug. So We have a CRTP container class, where the
So I want to use as:
where container-s
When it reaches Your example argument ("text", std::string) was not a valid reference (rvalue), so it is expected to contains a temporary object. This is not a
works well, without create any dangling reference.
|
The temporary
Here is a slightly modified example: https://godbolt.org/z/zbn6o5 |
I know what is happening, but your workaround is not an acceptable solution for us.
|
The issue is not about "How to fix our legacy code to work with this library defect", but "how to fix the library". |
@schaumb If the issue is with implicit conversions, does it go away in the latest release if you do this? #define JSON_USE_IMPLICIT_CONVERSIONS 0 |
@gregmarr No, as I wrote in my comment above. |
Ah, yes, I remember reading that now. Is there another implicit conversion that wasn't covered by that feature? |
I don't know what was the implicit conversion feature goal. https://github.com/nlohmann/json/blob/v3.9.1/single_include/nlohmann/json.hpp#L17927 But if it is explicit, many features will stop working (all complicated json construction). |
I'm not sure if this is a library defect. The assumption about perfect initialization of There is another workaround option that will compile with gcc 7 and leaves To preserve compatibility with legacy code that relies on existing signature of |
This "exotic" implicit conversion is not a good c++ practice. I am sure that this is a library defect, every other class (and other used json library) will pass a simple assumption that the developers (at least ours) thought:
We have a workaround for this issue, which I copied above, so we are not "search" any other. I want any of these as a solution:
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
. |
I think @karzhenkov already pointed out that lifetime prolongation for temporaries along std::forward_as_tuple is not guaranteed. Aint this the same problem category like using std::move to a local var in decltype(auto) function? Therefore the minimal reproduction example is already UB. @schaumb Do you agree? |
@igoloe So you would say that the issue is not related to the library? |
@nlohmann as far as i understand it, it is not a problem of the library. I think its possible to build an artifical reproduction scenario with custom types. For strings at least N bytes need to be used to bypass small string optimization and end up with dangling memory. I may check that, but can start least in 2 weeks. |
Summarize the problem: karzhenkov :
schaumb :
I am not want to create any temporary object.
No.
This UB is only with *I did not find any other commonly used c++ class that works like this (no std, no boost, no poco, no qt ...)
I am not agree with that.
Again the dummy example:
I still think this is a library related issue. |
It can easily be confirmed for clang 12.0 (debug, c++17) with nlohmann::json ver trunk on godbolt, if applying nlohmann::json j{};
auto const& cref{j};
std::tuple<nlohmann::json const&> const tuple1 = cref; // compile error, tries to move ... If you modify the style you write code and use a braced initialization, you do not fall into that trap and avoid conversions at all nlohmann::json j{};
auto const& cref{j};
std::tuple<nlohmann::json const&> const tuple1{cref}; // no move, as expected The problem arises from the resolution of the expression std::tuple<nlohmann::json const&> const tuple1 = cref; // compile error, tries to move ... where it looks like assigning This implies implicit ctors in Proof: nlohmann::json j{};
auto& ref{j};
using Tuple = std::tuple<nlohmann::json const&>;
Tuple const tuple1 = ref; // you are asking for the conversion ctor of tuple
static_assert(std::is_constructible_v<Tuple, nlohmann::json&>); // proof, conversion ctor taken
static_assert(!std::is_copy_constructible_v<Tuple, nlohmann::json&>);); // proof, direct initialization is not taken So we end up with the conversion ctor being selected and a dangling reference, because
@schaumb may work around, by using direct rather than conversion ctor. The library may fix that, by making A modification of the current behavior is a breaking change and can only be part of a major version. Related issues: #2583, #2311 and others i do not find. Story continues: Using msvc 16.7.18 (vs2019) with nlohmann::json 3.9.1 on local machine behaves differently: nlohmann::json j{};
auto const& cref{j};
std::tuple<nlohmann::json const&> const tuple1 = cref; // unexpected, not a compile error with msvc. I remember reading a thread, that clang, msvc and gcc may behave differently regarding ctor resolution. Thats quite odd. |
The exact problem was explained above.
gcc did not generate error or warning. |
So the problem is |
No.
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Is this still an issue? It should be noted I expect the behavior as described by the author of this post. However, I use brace initialization as shown in the example from above:
I suppose I could test this as well on the newest |
The issue is still exist: https://godbolt.org/z/qedM3ajW6 (with trunk gcc on master note:
|
I would like to second that turning off implicit conversions to This does not require anything to do with To reproduce, clone this repo at
The last line indicates that freed memory has been accessed. You can then checkout iFreilicht/json-questions@e081b14, which adds a single override for
As mentioned above, @schaumb as I understand, it would be enough to skip defining constructor (3) of |
I think it is enough to make it explicit (as clang-tidy recommends it), I don't think that it should be deleted. Note from me from the past:
|
Yeah that seems like the more sensible option. I've started working on a PR and will see how many tests this breaks (EDIT: Not a single one it seems). I think it might well be worth the trouble and explicitly calling the constructor here and there to get additional safety, considering these implicit conversions can lead to segfaults. Maybe it would make sense to have the switch for implicit conversions to |
Alright I created #4324 now to fix this. |
What is the issue you have?
I created a nlohmann::json, which I passed to a std::apply. When I wanted to get the json value at a parameter specified function (const nlohmann::json&), it referenced some undefined memory (g++).
I tried to simplify the code, when I came across the following:
std::tuple<const nlohmann::json&>::tuple(std::tuple<nlohmann::json&>&&)
constructor creates a temporary object and a dangling reference.This happens because
nlohmann::json
implicit conversion exists fromstd::tuple<nlohmann::json&>
, and the tuple constructor calls the variant (3) instead of (5).Can you provide a small but working code example?
try g++
With clang it doesn't compile:
tuple:232:24: error: reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object
What is the expected behavior?
g++: no assertion happens on the example code.
clang: compiles.
And what is the actual behavior instead?
g++: tuple creates a temporary object and creates a dangling reference.
clang: not compiles.
Which compiler and operating system are you using?
Which version of the library did you use?
develop
branch(but all version is affected)
If you experience a compilation error: can you compile and run the unit tests?
The text was updated successfully, but these errors were encountered: