cppfront
cppfront copied to clipboard
[BUG] Lookup of emitted definition differs from what's apparent in source code
Title: Type-scope object alias of nested type needs explicit qualification.
Description:
This is relevant to type-scope object alias emitted during Phase 2 "Cpp2 type definitions and function declarations" (all of them if #700 isn't fixed).
Lookup for the variable's type doesn't include the declaring class
because t:: hasn't been parsed.
Worse, there is a mismatch if lookup finds something else.
Of course, a: t::u == u(); would work,
but you wouldn't guess it just looking at Cpp2.
Minimal reproducer (https://cpp2.godbolt.org/z/oKP3Yev44):
// u: @struct type = { }
t: @struct type = {
u: @struct type = { }
a: u == u();
}
main: () = { }
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.
Actual result and error:
Cpp2 lowered to Cpp1:
//=== Cpp2 type declarations ====================================================
#include "cpp2util.h"
class t;
//=== Cpp2 type definitions and function declarations ===========================
// u: @struct type = { }
class t {
public: class u {};
public: static const u a;
};
auto main() -> int;
//=== Cpp2 function definitions =================================================
inline constexpr u t::a = u();
auto main() -> int{}
When lookup doesn't find u:
main.cpp2:4:20: error: 'u' does not name a type
When lookup finds another u:
main.cpp2:4:22: error: conflicting declaration 'constexpr const u t::a'
build/main.cpp:17:26: note: previous declaration as 'const t::u t::a'
17 | public: static const u a;
| ^
This is the same issue (or sufficiently similar) as pointed out by commit ca42e2c4a05753d35a9bc4d227413eac76c3872f.
Note: It's a known limitation that these won't yet work right on templated types. I will likely add that sometime in the future, and will require doing the dance Cpp1 requires to emit friend template specializations, and to handle dependent types more fully in
cpp2::in<>for cases likecpp2::in<mytype<T>>.
Well, that wasn't it.
I don't know the "dance".
Previously.
IIUC, it's so that both of these parameters are emitted equally (https://cpp2.godbolt.org/z/enPGfjx7a):
t: @struct <T: type> type = {
operator+: (_: t, _: t<T>) = { }
}
main: () = { }
It currently emits:
template <typename T> auto operator+([[maybe_unused]] cpp2::in<t> param1, [[maybe_unused]] cpp2::in<t<T>> param2) -> void{}
But should be
template <typename T> auto operator+([[maybe_unused]] cpp2::in<t<T>> param1, [[maybe_unused]] cpp2::in<t<T>> param2) -> void{}
Because t is the injected-class-name.
It's like templated @enums not working (https://cpp2.godbolt.org/z/vPGj1sKvn):
engine: @enum <T: type> type = {
off;
on;
stuff_on_t: (this) -> T = T();
}
main: () = { }
main.cpp2:24:25: error: unknown type name 'engine'; did you mean 'inline'?
24 | inline constexpr engine engine::off = 0;
| ^~~~~~
| inline
main.cpp2:26:25: error: unknown type name 'engine'; did you mean 'inline'?
26 | inline constexpr engine engine::on = 1;
| ^~~~~~
| inline
main.cpp2:34:67: error: use of class template 'engine' requires template arguments
34 | template <typename T> auto operator<<(std::ostream& o, cpp2::in<engine> val) -> std::ostream&{o << CPP2_UFCS_0(to_string, val);return o; }
| ^
build/main.cpp:13:28: note: template is declared here
13 | template<typename T> class engine {
| ~~~~~~~~~~~~~~~~~~~~ ^
3 errors generated.
ninja: build stopped:
There seems to be a simpler solution accepted by the supported compilers.
From the generated code of the reproducer,
replacing inline CPP2_CONSTEXPR u t::a = u();
with inline CPP2_CONSTEXPR decltype(t::a) t::a = u(); works:
https://compiler-explorer.com/z/a14P79Yar.
Anyways, the same issue pops up again when you use a name in myclass before the lowered myclass:: qualifier (https://cpp2.godbolt.org/z/z3WM6ovsb):
// u: @struct type = { }
v: @struct <T> type = { }
t: @struct type = {
u: @struct type = { }
a: <_: v<u>> int == 0;
}
main: () = { }
//=== Cpp2 type definitions and function declarations ===========================
public: template<v<u> _> static const int a;
//=== Cpp2 function definitions =================================================
template<v<u> _> inline CPP2_CONSTEXPR int t::a = 0;
main.cpp2:5:14: error: use of undeclared identifier 'u'
5 | template<v<u> _> inline CPP2_CONSTEXPR int t::a = 0;
| ^
main.cpp2:5:17: error: template non-type parameter has a different type 'int' in template redeclaration
5 | template<v<u> _> inline CPP2_CONSTEXPR int t::a = 0;
| ^
main.cpp2:5:25: note: previous non-type template parameter with type 'v<u>' is here
5 | public: template<v<u> _> static const int a;
| ^
2 errors generated.
Using a function (https://cpp2.godbolt.org/z/3hbPMT5ar):
// u: @struct type = { }
v: @struct <T> type = { }
t: @struct type = {
u: @struct type = { }
f: <_: v<u>> () = { }
}
main: () = { }
main.cpp2:5:14: error: use of undeclared identifier 'u'
5 | template<v<u> _> auto t::f() -> void{}
| ^
main.cpp2:5:28: error: out-of-line definition of 'f' does not match any declaration in 't'
5 | template<v<u> _> auto t::f() -> void{}
| ^
2 errors generated.