cppfront icon indicating copy to clipboard operation
cppfront copied to clipboard

[BUG] Type-scope object alias can't have deduced type or use CTAD

Open JohelEGP opened this issue 2 years ago • 11 comments

Title: Can't use CTAD for type-scope object alias.

Minimal reproducer (https://cpp2.godbolt.org/z/63occKT7x):

#include <array>
arr0: std::array == :std::array = (0);; // OK.
t: @struct type = {
  arr1: std::array == :std::array = (0);; // error.
}
main: () = { }
Commands:
cppfront main.cpp2
clang++17 -std=c++23 -stdlib=libc++ -lc++abi -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -I . main.cpp

Expected result:

A definitely not-incomplete object type to be defined in the class' body.

  public: static constexpr std::array arr1 = std::array{0};

Actual result and error:

//=== Cpp2 type definitions and function declarations ===========================
  public: static const std::array arr1;   // error.
//=== Cpp2 function definitions =================================================
  inline constexpr std::array t::arr1 = std::array{0};
Cpp2 lowered to Cpp1:


//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"


class t;
  

//=== Cpp2 type definitions and function declarations ===========================

#include <array>
std::array inline constexpr arr0 = std::array{0};// OK.
class t {
  public: static const std::array arr1;   // error.
};
auto main() -> int;


//=== Cpp2 function definitions =================================================


  inline constexpr std::array t::arr1 = std::array{0};

auto main() -> int{}
Output:
main.cpp2:4:35: error: declaration of variable 'arr1' with deduced type 'const std::array' requires an initializer
    4 |   public: static const std::array arr1;   // error.
      |                                   ^
1 error generated.

See also:

  • Commit b589f5d25e5acdbfd94791e23c0ba0f6fdcd59c6.
  • #687.
  • #666 for more of "definitely not-incomplete object type".

JohelEGP avatar Sep 24 '23 01:09 JohelEGP

Title: Type-scope object alias can't have deduced type.

Description:

This is what I used before commit b589f5d25e5acdbfd94791e23c0ba0f6fdcd59c6.

Minimal reproducer (https://cpp2.godbolt.org/z/jhnjWPY76):

#include <array>
arr0 :== :std::array = (0);; // OK.
t: @struct type = {
  arr1 :== :std::array = (0);; // error.
}
main: () = { }
Commands:
cppfront main.cpp2
clang++17 -std=c++23 -stdlib=libc++ -lc++abi -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -I . main.cpp

Expected result:

  public: static const auto arr1 = std::array{0};

Actual result and error:

//=== Cpp2 type definitions and function declarations ===========================
  public: static const auto arr1;// error.
//=== Cpp2 function definitions =================================================
  inline constexpr auto t::arr1 = std::array{0};
Cpp2 lowered to Cpp1:


//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"


class t;
  

//=== Cpp2 type definitions and function declarations ===========================

#include <array>
auto inline constexpr arr0 = std::array{0};// OK.
class t {
  public: static const auto arr1;// error.
};
auto main() -> int;


//=== Cpp2 function definitions =================================================


  inline constexpr auto t::arr1 = std::array{0};

auto main() -> int{}
Output:
main.cpp2:4:29: error: declaration of variable 'arr1' with deduced type 'const auto' requires an initializer
    4 |   public: static const auto arr1;// error.
      |                             ^
1 error generated.

JohelEGP avatar Sep 24 '23 01:09 JohelEGP

One limitation of first declaring and then defining a type-scope object alias is that implementations inconsistently accept their use as constexpr (tested with #703). Defining in the class body always works: https://compiler-explorer.com/z/vabrK137E. Otherwise, MSVC, Clang, and GCC give different results: https://compiler-explorer.com/z/zcdabTE4T. For completeness, here's the previous one, but at namespace scope: https://compiler-explorer.com/z/rsabsnjGK. (I'm polling their correctness at https://cpplang.slack.com/archives/C21PKDHSL/p1695693565690049).

JohelEGP avatar Sep 26 '23 02:09 JohelEGP

I think that means that a templated @enum is bound to fail (on Clang and MSVC) when you need an enumerator during constant evaluation. I tried to actually test it, but a type-scope @enum fails: https://cpp2.godbolt.org/z/eheWhYYPz.

machine: @struct <Engine: type> type = {
  states: @enum = {
    on;
    off;
  }
}
main: () = { }
cppfront: source/cppfront.cpp:1870: void cpp2::cppfront::emit(const cpp2::compound_statement_node&, const cpp2::function_prolog&, const std::vector<std::__cxx11::basic_string<char> >&): Assertion `!current_functions.empty()' failed.
Program terminated with signal: SIGSEGV
Compiler returned: 139

JohelEGP avatar Sep 26 '23 02:09 JohelEGP

Looks like the limitation was known. From the linked SO: https://stackoverflow.com/a/32134757.

JohelEGP avatar Sep 26 '23 11:09 JohelEGP

Interim ack: In the meantime I'll add a diagnostic for an object alias in a nested type, which isn't currently supported. Thanks!

hsutter avatar Sep 26 '23 23:09 hsutter

Interim ack: In the meantime I'll add a diagnostic for an object alias in a nested type, which isn't currently supported. Thanks!

I think that's incidentally fixed by #703.

JohelEGP avatar Sep 27 '23 23:09 JohelEGP

Interim ack: In the meantime I'll add a diagnostic for an object alias in a nested type, which isn't currently supported. Thanks!

I think that's incidentally fixed by #703.

It actually doesn't. But it's the same issue as #704. Except that #706 fixes it for non-@enum aliases by defining them in the class body.

JohelEGP avatar Sep 29 '23 14:09 JohelEGP

(I'm polling their correctness at https://cpplang.slack.com/archives/C21PKDHSL/p1695693565690049).

According to Brian Bi at https://cpplang.slack.com/archives/C21PKDHSL/p1696124673621459?thread_ts=1695800824.887719&cid=C21PKDHSL:

I'm discussing it with folks on the reflector. It should be valid, even though Clang/MSVC don't accept it yet.

So it seems those are just compiler bugs.

JohelEGP avatar Oct 01 '23 02:10 JohelEGP

Thanks!

Looks like the limitation was known. From the linked SO: https://stackoverflow.com/a/32134757.

Right, and because Clang 3.4 is so old I just assumed that by now it would have been fixed. But it seems it's a rare enough case that it hasn't. Thank you very much for following up on this!

hsutter avatar Oct 01 '23 16:10 hsutter

A definitely not-incomplete object type to be defined in the class' body.

This is troublesome to determine. Heuristics can only get you so far. For example, #706 can't solve this:

t: @struct type = {
  a: std::optional<t>   == (); // Should be defined out of class.
  b: std::optional<* t> == (); // Can be defined in class.
}

The heuristics of #706 are that those are defined in class. It gets worse the more indirections one introduces:

f: () -> std::optional<t>   == ();
g: () -> std::optional<* t> == ();
t: @struct type = {
  a :== f(); // Should be defined out of class, but has a deduced type.
  b :== g(); // Can be defined in class (has a deduced type, so it should?).
}

JohelEGP avatar Oct 31 '23 15:10 JohelEGP

This is a limitation of Cppfront.

A simpler heuristic that should always works is to define in class when the type is a placeholder. So when you need to use CTAD, or use the value in another member's signature, you just need to use the placeholder type.

Done in #706.

JohelEGP avatar Oct 31 '23 21:10 JohelEGP