cppfront
cppfront copied to clipboard
[BUG] UFCS with variable template in scope fails
Title: UFCS with variable template in scope fails.
Description:
std::vector<int>().empty() will fail given using std::views::empty;.
That should end up calling the member empty of std::vector.
But the UFCS macro fails on the free function branch.
All compilers fail: https://compiler-explorer.com/z/bWejqfodq. With #506, MSVC starts accepting: https://compiler-explorer.com/z/TYaMW7av6.
Minimal reproducer (https://cpp2.godbolt.org/z/7nfzr7zYx):
#include <ranges>
#include <vector>
using std::views::empty;
auto _ = std::vector<int>().empty();
main: () = {
_ = std::vector<int>().empty();
}
Commands:
cppfront main.cpp2
clang++18 -std=c++23 -stdlib=libc++ -lc++abi -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -I . main.cpp
Expected result: A well-formed program that calls the member empty of std::vector.
Actual result and error:
Cpp2 lowered to Cpp1:
//=== Cpp2 type declarations ====================================================
#include "cpp2util.h"
//=== Cpp2 type definitions and function declarations ===========================
#include <ranges>
#include <vector>
using std::views::empty;
auto _ = std::vector<int>().empty();
auto main() -> int;
//=== Cpp2 function definitions =================================================
auto main() -> int{
static_cast<void>(CPP2_UFCS_0(empty, std::vector<int>()));
}
Output:
main.cpp2:6:33: error: use of variable template 'empty' requires template arguments
6 | static_cast<void>(CPP2_UFCS_0(empty, std::vector<int>()));
| ^
/opt/compiler-explorer/clang-trunk-20231104/bin/../include/c++/v1/__ranges/empty_view.h:45:36: note: template is declared here
44 | template <class _Tp>
| ~~~~~~~~~~~~~~~~~~~~
45 | inline constexpr empty_view<_Tp> empty{};
| ^
1 error generated.
I noticed the same failure if you attempt to call std::vector<int>().std::vector<int>::empty() (https://cpp2.godbolt.org/z/c5veMnjMe).
Branch main: https://compiler-explorer.com/z/9oa1Ga3Tx (but see #555).
#506: https://compiler-explorer.com/z/qvK7E4Kj8.
Except that Clang's error message requests for disambiguating template.
This issue came up while implementing @rule_of_zero at #797.
Fortunately, I could fix it with a local using std::ranges::empty; // Pending #506.
(std::views::empty actually came from using namespace std::views;).
So it doesn't directly use std::vector::empty.
I was fortunate enough to have such a fix-hack at hand.
I think this one really is IFNDR.
Reading https://eel.is/c++draft/algorithms.requirements#2
reminded me that the else branch,
although never taken for std::vector,
will always be ill-formed for all invocations (because std::views::empty needs a template argument).
IIRC from my analysis at #307, it's that this can't be solved without Cpp1 reflection.
The same seems to happen for operator(): https://compiler-explorer.com/z/65Y5rh7P5.
The one case we can handle is when source local name lookup results in a variable.
If it isn't templated, func<targs>(obj) can't be valid, so the only option is the member call.
See https://cpp2.godbolt.org/z/T6sdezYKh:
t: @struct type = {
f: <T> (this) = { }
g: <T> (this) = { }
}
main: () = {
t().f<int>(); // OK.
g := 0;
// error: 'g' does not name a template but is followed by template arguments
t().g<int>();
_ = g;
}
auto main() -> int{
CPP2_UFCS_TEMPLATE(f<int>)(t());// OK.
auto g {0};
CPP2_UFCS_TEMPLATE(g<int>)(t());// S
static_cast<void>(std::move(g));
}
main.cpp2:10:22: error: 'g' does not name a template but is followed by template arguments
10 | CPP2_UFCS_TEMPLATE(g<int>)(t());
| ^~~~~~
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:855:84: note: expanded from macro 'CPP2_UFCS_TEMPLATE'
855 | #define CPP2_UFCS_TEMPLATE(...) CPP2_UFCS_(&,(),template,__VA_ARGS__)
| ^~~~~~~~~~~
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:845:51: note: expanded from macro 'CPP2_UFCS_'
845 | noexcept(CPP2_UFCS_IS_NOTHROW_ARG(QUALID,TEMPKW,__VA_ARGS__)) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) \
| ^~~~~~~~~~~
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:803:90: note: expanded from macro 'CPP2_UFCS_IS_NOTHROW_ARG'
803 | #define CPP2_UFCS_IS_NOTHROW_ARG(QUALID,TEMPKW,...) CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,__VA_ARGS__)
| ^~~~~~~~~~~
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:801:60: note: expanded from macro 'CPP2_UFCS_IS_NOTHROW'
801 | requires noexcept(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__(std::declval<Obj>(), std::declval<Params>()...)); }
| ^~~~~~~~~~~
main.cpp2:8:8: note: non-template declaration found by name lookup
8 | auto g {0};
| ^