Skip to content

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

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

Open
JohelEGP opened this issue Sep 26, 2023 · 6 comments
Labels
bug Something isn't working

Comments

@JohelEGP
Copy link
Contributor

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 JohelEGP added the bug Something isn't working label Sep 26, 2023
@JohelEGP
Copy link
Contributor Author

JohelEGP commented Sep 27, 2023

This is the same issue (or sufficiently similar) as pointed out by commit ca42e2c.

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Contributor Author

JohelEGP commented Jan 9, 2024

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 JohelEGP changed the title [BUG] Type-scope object alias of nested type needs explicit qualification [BUG] Lookup of emitted definition differs from what's apparent in source code Jan 9, 2024
@JohelEGP
Copy link
Contributor Author

Now we have a local name lookup algorithm.
We can identify when we're lowering a name local to a type
during Phase 2 "Cpp2 type definitions and function declarations"
and before the my_class:: in the declaration's name
to appropriately qualify the name.

Taking the example above,
when lowering template<v<u> _> auto t::f() -> void{},
when lowering the v and u, we're still before t::,
so those are looked up in t.
Only u has a match, so that gets t:: prepended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant