Skip to content

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

Open

Description

@JohelEGP

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:

Activity

  1. JohelEGP commented on Sep 24, 2023

    @JohelEGP
    ContributorAuthor

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

    Description:

    This is what I used before commit b589f5d.

    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.
    
  2. changed the title [-][BUG] Can't use CTAD for type-scope object alias[/-] [+][BUG] Type-scope object alias can't have deduced type or use CTAD[/+] on Sep 24, 2023
  3. JohelEGP commented on Sep 26, 2023

    @JohelEGP
    ContributorAuthor

    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).

  4. JohelEGP commented on Sep 26, 2023

    @JohelEGP
    ContributorAuthor

    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
    
  5. JohelEGP commented on Sep 26, 2023

    @JohelEGP
    ContributorAuthor

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

  6. hsutter commented on Sep 26, 2023

    @hsutter
    Owner

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

  7. JohelEGP commented on Sep 27, 2023

    @JohelEGP
    Author
  8. JohelEGP commented on Sep 29, 2023

    @JohelEGP
    ContributorAuthor

    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.

  9. JohelEGP commented on Oct 1, 2023

    @JohelEGP
    ContributorAuthor

    (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.

  10. hsutter commented on Oct 1, 2023

    @hsutter
    Owner

    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!

  11. JohelEGP commented on Oct 31, 2023

    @JohelEGP
    ContributorAuthor

    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?).
    }
    
  12. JohelEGP commented on Oct 31, 2023

    @JohelEGP
    ContributorAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions