-
Notifications
You must be signed in to change notification settings - Fork 12.1k
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
[concepts] deferred substitution into requirements of class template members not implemented #44178
Comments
The difference between GCC and Clang is due to an open question for C++20 concepts: do we defer substitution into member requires-clauses or not? The current thinking in the committee is that we do not, which is what Clang implements (the older direction was that we would defer such substitution, which is what GCC implements). You can avoid this problem by using a named concept instead of an inline requires-clause: https://godbolt.org/z/XCjyw_ |
(Using a named concept allows the code to be written a bit more concisely too: https://godbolt.org/z/QsSnXM) |
Thank you Richard Smith for the quick response. So if I understand you correctly an inline requires is currently okay, if I don't use anything declared within the context of the class. That means
Do you know what the voting in the last meeting on this was and what the resolution is? As a user I think of inline requires (if you don't use them in combination with named concepts) as glorified enable_if's with better diagnostic output. Because subsumption can only take place with named concepts, so I personally would prefer if they behave the same. This code was reduced and was originally part of a macro that made CPO overloads easier, if I have to use named concepts this would defeat the purpose of that macro. #define SEQAN3_CPO_IMPL(PRIO, TERM) \
template <typename t, typename ...arg_ts> \
static constexpr decltype(auto) impl(seqan3::detail::priority_tag<PRIO> const &, t && v, [[maybe_unused]] arg_ts && ... args) \
requires requires (seqan3::detail::priority_tag<PRIO> const &, t && v, arg_ts && ... args) \
{ { TERM }; } \
{ return TERM; } But maybe we have to find a different solution. On a side note, none of my/our solutions work with msvc. |
The rule that we decided on is this:
This is closer to GCC's behavior than to Clang's, and in particular means your example is valid and this is a Clang bug. |
+1, stumbled across this implementing std::ranges::view_interface (which is specified to expect "Thou shalt not substitute into requires-clauses before overload resolution!"). |
Is this the same issue that prevents the use of ranges from GCC 10.2.0 libstdc++? Which boils down to:
|
*** Bug llvm/llvm-bugzilla-archive#47508 has been marked as a duplicate of this bug. *** |
+1 Having to use a named concept or boolean variable is counter-intuitive. |
I believe this is a genuine bug, as the standard seems to expect that the Those |
Thanks for the reproducer! I've poked around this for a few days, and this example is pretty clarifying. In this case, the constraint fails because we've been evaluating it immediately upon instantiation of the class (in the definition of 'Test''s inheritence), rather than waiting until it is called. I see that Richard posted about it here:
Which I think is likely the solution to the ranges problem as well! Unfortunately, it is a pretty significant architectural change to make this happen as far as I can tell. I'm still looking into it, as compiling ranges is pretty important for us. |
Hi Richard- |
|
*** #49634 has been marked as a duplicate of this bug. *** |
Is there any progress on this issue? Does it indeed break all usage of views from libstdc++? |
mentioned in #46852 |
mentioned in #49634 |
mentioned in #50208 |
Sorry @h-2 that this took a while to respond to. My understanding is yes, this breaks a significant portion of the 'ranges' views (and much of the rest of ranges as well). I looked at it for a while and made only a minimum amount of progress, not enough to consider it 'progress'. I can disable the premature instantiation of the concept, but cannot figure out how to properly re-instantiate it when it needs to be done. I suspect someone who understands the template instantiation engine better than I (like @zygoloid ) would have to either give some extremely good hints, or just do it themselves :( |
Thanks for the update! It seems like this is really a showstopper for lots of C++20 goodness, but there is not much I can do to help. If you do get any experimental code, I would of course be willing to test it. |
As an update... I'm still working on this as my non-emergency-top-priority type thing. I FEEL like I'm making progress, but I've still got 5 tests that crash. |
I FINALLY got a version together that doesn't assert/crash on any of the lit tests, but I'm sure there are at least one issue left: https://reviews.llvm.org/D119544 I currently have 7 lit test failures, and some seem to be that some template arguments are reversed for some reason. I'll need to take a look at those, but this is at least something that could be 'played with'. |
The tool detection will also be needed on the pipette. In addition to therefore needing the tool detection code to live in common/ somewhere, the pipettes also need to interact with the code in a different way - they don't care what kind of tool they are (or maybe do only as a debug), just what carrier they're on. In the same way, the head doesn't really care what carrier it reads, since it knows which pins it's looking at, just which tool it is. So we can provide separate filtering options for each and let each use of the code become simpler. Unfortunately ranges and views just don't work in clang at all because of llvm/llvm-project#44178 so even though it all compiles correctly we have to do a pretty ugly hack that removes functionality when linting. It would be nice to not have this.
The tool detection will also be needed on the pipette. In addition to therefore needing the tool detection code to live in common/ somewhere, the pipettes also need to interact with the code in a different way - they don't care what kind of tool they are (or maybe do only as a debug), just what carrier they're on. In the same way, the head doesn't really care what carrier it reads, since it knows which pins it's looking at, just which tool it is. So we can provide separate filtering options for each and let each use of the code become simpler. Unfortunately ranges and views just don't work in clang at all because of llvm/llvm-project#44178 so even though it all compiles correctly we have to do a pretty ugly hack that removes functionality when linting. It would be nice to not have this.
- refactor(head,common): commonize tool detect The tool detection will also be needed on the pipette. In addition to therefore needing the tool detection code to live in common/ somewhere, the pipettes also need to interact with the code in a different way - they don't care what kind of tool they are (or maybe do only as a debug), just what carrier they're on. In the same way, the head doesn't really care what carrier it reads, since it knows which pins it's looking at, just which tool it is. So we can provide separate filtering options for each and let each use of the code become simpler. Unfortunately ranges and views just don't work in clang at all because of llvm/llvm-project#44178 so even though it all compiles correctly we have to do a pretty ugly hack that removes functionality when linting. It would be nice to not have this. - refactor(head,common): commonize adc support There's some ADC stuff that's different between devices (well, a lot of ADC stuff) but the reading->mv transition isn't, so we might as well commonize it. The core element here is an ADC channel, which is templatized with the conversion variables. That lets it do some nice conversions, and with some required implementations in the child we can have a nice generalizable interface. There isn't an ADC interface in common, because that's going to go a bunch of different places - the head has one, for instance, that specifically has gripper, z, and a channels - nothing else needs that. It relies on the common channel implementation, and the simulation and test versions override as appropriate. - feat(pipettes): Detect current mount at boot Based on the voltage present on the tool sense pin and using the ADC and mount detection utilities previously added to common/, we can now pretty easily decide which mount we're on and assign ourselves a canbus id based on that. - feat(bootloader): add pipette mount detection This unfortunately reimplements the mount detection algorithm in C but it will work just fine (and is checked by unit tests). Bootloaders should now properly decide which mount they're on. - fix(bootloader): allow flashing pipettes This flash command had the wrong openocd config value
FWIW, I was able to get by all of the lit tests today on this patch and tested a handful of ranges examples (including the one from @llvmbot here) https://reviews.llvm.org/D119544. |
Patch re-committed as yesterday, but I'm not going to be brave enough to mark this 'closed' for a few more days at minimum :) |
Alright, we made it a week now and only have 2 different regressions reported. One was fixed immediately, the other (#57958) is under review. So I think I'm far enough along to have SOME faith this isn't going to be reverted. Thanks all for the help with testing, and the reports in the first place! |
@erichkeane any chance of this getting back-ported to LLVM/Clang 13? |
None whatsoever unfortunately. There have been a number of regressions thanks to this (that I've been working through as they come up), and I'm sure there are likely more. This would be too high risk to backport to any of the 13-15 branches. So if you want to use this, you'll have to update to Clang 16 when it comes out. |
Thanks @erichkeane. I didn't think so, but I figured it won't hurt to ask. 🤷🏻♂️ 😃 |
It still dont work for https://godbolt.org/z/r77oWoMTr May be in can be easily fixed (based on previous fix)? |
Interesting, thanks for pointing this out! @royjacobson was poking around default function constraints at one point as well, so he might have some idea. |
I see that the code that calls this is part of EDIT: |
The standard is clear that we can't defer constraints checks for destructors, but P0848 is unclear about when, or if, to perform the constraints checks for the other special member functions. When I implemented P0848 I decided to preemptively evaluating the constraints when we complete a class, and that it's probably OK. Not doing it would've added a non-trivial effort to support deferred computation of But maybe this is non compliant and we really should defer the triviality calculations. |
Hmm... that seems worthy of a CWG reflector discussion? Mind starting one? |
I don't think we should, or reasonably can, defer these calculations in general. This seems to be the same kind of situation as in CWG1344, which described another way in which the set of special member functions could change after the class becomes complete. |
The combination of clang <= 15 and libstdc++'s version of <ranges> runs into llvm/llvm-project#44178.
Using std::ranges::views::reverse breaks when compiling with clang and libstdc++ as per llvm/llvm-project#44178.
…on will not work when concepts are in use until Clang 16 at the earliest. See: llvm/llvm-project#44178 (comment)
Extended Description
Sorry for this verbose and hard to read code, but I wanted to make this configurable.
This code basically has multiple code paths. If REQUIRES_BUG is set, the impl function uses requires to infer if char_is_valid_for is defined or not. If not impl uses SFINAE.
As you can see on https://godbolt.org/z/tyeiJ2 when defining
clang++ -std=c++2a -DREQUIRES_BUG=1 -DFUNCTION_DEFINED=0
the requires block should silently see that the requires is not valid (because of a deleted function) and skip to the next definition.Error Message:
Interestingly, if you substitute
s_alph_t
withalph_t;
everything works as expected.The text was updated successfully, but these errors were encountered: