Just exit instead.
Fixes https://github.com/llvm/llvm-project/issues/42535
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
clang/lib/Sema/SemaOverload.cpp | ||
---|---|---|
14006 | It feels a bit weird that we are succeeding in finding the best viable function and what we find is invalid. |
clang/lib/Sema/SemaOverload.cpp | ||
---|---|---|
14006 | Yes, but it seems to be done this way in other places as well: |
clang/lib/Sema/SemaOverload.cpp | ||
---|---|---|
14006 | It looks like we have a couple of test that generate the cannot be variadic diagnostic for overloads e.g. clang/test/SemaCXX/overloaded-operator-decl.cpp it might be worth looking into why they don't crash and this case does. |
clang/lib/Sema/SemaOverload.cpp | ||
---|---|---|
14006 | I see that but that diagnostic looks like it is generated by the call to CheckMemberOperatorAccess(...) which is not really the same situation we have here. |
clang/lib/Sema/SemaOverload.cpp | ||
---|---|---|
14006 | We crash when ask functiondecl its second parameter and it doesn't have it when the call expression is formed. |
clang/lib/Sema/SemaOverload.cpp | ||
---|---|---|
14006 |
Is it? For me it seems there is a similar exit inside of BuildCallToObjectOfClassType function whose description says: /// BuildCallToObjectOfClassType - Build a call to an object of class /// type (C++ [over.call.object]), which can end up invoking an /// overloaded function call operator (@c operator()) or performing a /// user-defined conversion on the object argument. CheckMemberOperatorAccess is just a call above. |
clang/lib/Sema/SemaOverload.cpp | ||
---|---|---|
14006 |
I don't think it's all that weird. Consider: void overloaded(int); void overloaded(float) noexcept(error()); int main() { overloaded(1.0f); } the best viable function is definitely the one taking a float, but the declaration itself is still invalid. | |
clang/test/SemaCXX/overloaded-operator-decl.cpp | ||
65 | I think it might make sense to extend the test coverage for the other operators you can overload, just to demonstrate we diagnose them all consistently. WDYT? |
clang/lib/Sema/SemaOverload.cpp | ||
---|---|---|
14006 |
I think the fact that we have cases for which this diagnostic fires but does not crash deserved some digging into to see why those cases avoid this situation. If it ends up they are doing something similar then we can feel more confident that this is the right approach. |
clang/lib/Sema/SemaOverload.cpp | ||
---|---|---|
14006 | I thought we already knew what was going on there?
|
clang/test/SemaCXX/overloaded-operator-decl.cpp | ||
---|---|---|
65 | Okay, while trying to add more test cases I discovered that following class E {}; bool operator<(const E& lhs, ...); auto operator<=>(const E& lhs, ...); void d() { E() < E(); } crashes even with the patch since there is code searching for best overload candidate that doesn't consider possibility for them making variadic. |
clang/test/SemaCXX/overloaded-operator-decl.cpp | ||
---|---|---|
65 | Overload resolution doesn't need to produce a candidate that's viable to call; C++ lets you resolve to the "best viable function" only to say "and that one wasn't good enough either." e.g., http://eel.is/c++draft/over#match.general-3 I've not yet spotted anything in http://eel.is/c++draft/over that says invalid declarations should/should not be added to the initial candidate set. I *think* the intention is that if name lookup can find the name, it goes into the candidate set. Then that set is processed to remove functions that are not viable (http://eel.is/c++draft/over.match.viable). Then we find the best viable function from that set. I think we should be keeping the function in the candidate set so long as it matches the rules in http://eel.is/c++draft/over.match.viable even if the function is otherwise not viable. Otherwise, correcting an unrelated issue might change overload resolution to find a completely different function. e.g., in my example above, we'd select void overloaded(int); as the best viable function, but when the user corrects the float function, we'd change to call that instead. I think it's easier to understand what's going on when picking the float overload to begin with and saying "but we can't call that because it's busted". CC @cor3ntin @hubert.reinterpretcast @rsmith for some extra opinions, as I'm not certain if I'm interpreting the standard correctly or not. |
clang/test/SemaCXX/overloaded-operator-decl.cpp | ||
---|---|---|
65 | I'm not sure how much the standardese matter here as the declaration of the operators are ill-formed anyway. |
clang/test/SemaCXX/overloaded-operator-decl.cpp | ||
---|---|---|
65 | Thanks for the extra perspective! Yeah, I think that's what I've been convincing myself of. Effectively: int overloaded(int); float overloaded(float) noexcept(error()); // error: invalid declaration int main() { (void)overloaded(1.0f); // #1 } We're always going to get the invalid declaration diagnostic. The question is whether we want a diagnostic at #1 that says "can't call this overload" or whether we want #1 to have no diagnostic (because it picked the int overload). From a purely diagnostic perspective, I think I can see arguments either way. But if we change it slightly: template <typename Ty> constexpr int func() { return 0; } template <> constexpr int func<int>() { return 1; } template <> constexpr int func<float>() { return 2; } int overloaded(int); float overloaded(float) noexcept(error()); // error: invalid declaration static_assert(func(overloaded(1.0f)) == 2); // #1 we still get the invalid declaration diagnostic, but now we get a failing static assertion because we picked the int overload rather than the float overload. I think this is clearly worse behavior than if we issued an additional diagnostic at #1 saying that the overload we picked (float) was invalid. https://godbolt.org/z/34aGMY43n WDYT? |
clang/test/SemaCXX/overloaded-operator-decl.cpp | ||
---|---|---|
65 | Agreed. If i write an overload i presumably want the compiler to consider it, even if I botched it. And apparently, the issue is not that we don't do that but rather than we don't always do that. We should strive to be consistent. It's not critical because even in the static_assert case, the only thing a user can do is fix the declaration, at which point they would get the ambiguity diag or the correct best match correctly picked. But it's nicer to have more diags, when we can |
clang/test/SemaCXX/overloaded-operator-decl.cpp | ||
---|---|---|
65 | I see, thank you both for the extensive explanation. |
Rebase, add more test cases, fix assertion on variadic functions inside of
haveSameParameterTypes
clang/test/SemaCXX/overloaded-operator-decl.cpp | ||
---|---|---|
65 | Ok, added a bit more test cases. |
Hmm, clang-format reports failure, but applying git-clang-format to the commit doesn't give me anything. It seems the whole SemaOverload.cpp file has broken formatting, maybe that is the reason for the failure.
It feels a bit weird that we are succeeding in finding the best viable function and what we find is invalid.