cppfront icon indicating copy to clipboard operation
cppfront copied to clipboard

[BUG] Lookup of emitted definition differs from what's apparent in source code

Open JohelEGP opened this issue 2 years ago • 6 comments
trafficstars

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{}
Output:

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;
      |                          ^

JohelEGP avatar Sep 26 '23 11:09 JohelEGP

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 like cpp2::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.

JohelEGP avatar Sep 27 '23 01:09 JohelEGP

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:

JohelEGP avatar Sep 27 '23 01:09 JohelEGP

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.

JohelEGP avatar Sep 29 '23 18:09 JohelEGP

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.

JohelEGP avatar Sep 29 '23 20:09 JohelEGP

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.

JohelEGP avatar Jan 09 '24 16:01 JohelEGP