Hi!

I've been trying to make
#include <meta>
#include <string>
#include <iostream>

template<typename S>
constexpr std::string serialize(S s) {
  constexpr auto ctx = std::meta::access_context::current();
  std::string result = " ";
  template for (constexpr auto m : 
std::define_static_array(nonstatic_data_members_of(^^S, ctx))) {
    result += identifier_of(m);
    result += "=";
    if constexpr (type_of(m) == ^^int)
      result += std::string(s.[:m:] / 10, 'X');
    else
      result += s.[:m:];
    result += " ";
  };
  return result;
}

struct Person {
  std::string name;
  int age;
};

constexpr Person john{"John", 42};

static_assert(serialize(john) == " name=John age=XXXX ");

int main() {
  using namespace std;
  string s = "john:"; 
  s += serialize(john);
  cout << s << endl;
}
work on Marek's reflection branch today, unfortunately it doesn't work.

I've manually reduced the problem to something which doesn't need
reflection:
#include <span>

constexpr int arr1[3] = { 1, 2, 3 };
consteval std::span <const int> foo () { return std::span <const int> (arr1); }

void
bar ()
{
#ifdef USE_EXPANSION_STMT
  template for (constexpr auto m : foo ())
    ;
#else
  static constexpr auto &&range = foo ();
  static constexpr auto begin = range.begin ();
  static constexpr auto end = range.end ();
  static constexpr auto N = [] consteval {
    std::ptrdiff_t result = 0;
    for (auto i = begin ; i != end ; ++i) ++result;
    return result;
  }();
  static_assert (N == 3);
  {
    static constexpr auto iter = begin + decltype(begin - 
begin){std::ptrdiff_t(0)};
    constexpr auto m = *iter;
  }
  {
    static constexpr auto iter = begin + decltype(begin - 
begin){std::ptrdiff_t(1)};
    constexpr auto m = *iter;
  }
  {
    static constexpr auto iter = begin + decltype(begin - 
begin){std::ptrdiff_t(2)};
    constexpr auto m = *iter;
  }
#endif
}
where I've included also the desugarized version of the expansion stmt
(note, it is partly before https://cplusplus.github.io/CWG/issues/3044.html
because we don't still support constexpr references and so it can't
be non-static, but I guess with non-static the behavior will be the same).

The desugarized version fails to compile with both GCC trunk and clang,
the function returns a non-const value which needs to be stored into a
lifetime extended temporary and clearly both compilers create
static std::span <const int> variable for that, so obviously the rest of
the constant evaluation doesn't work.

Now, clang++ can be helped with changing
  static constexpr auto &&range = foo ();
line to
  static constexpr const auto &&range = foo ();
(and I wonder if https://eel.is/c++draft/stmt.expand#5.2 shouldn't be
changed accordingly to be
... constexpr const auto&& range = expansion-initializer;
).  Note Jonathan suggested that perhaps in that case it should be
... constexpr const auto& range = expansion-initializer;
instead.
This doesn't help g++ though.  For that one only:
  static constexpr auto &&range = (const std::span <const int>) foo ();
works, set_up_extended_ref_temp uses the type of expr (i.e.
std::span <const int> here) rather than TREE_TYPE (TREE_TYPE (decl))
i.e. if range was constexpr const auto && then const std::span <const int>,
and uses that type to create the temporary as well as in
  if (TREE_CONSTANT (init))
    {
      if (literal_type_p (type)
          && CP_TYPE_CONST_NON_VOLATILE_P (type)
          && !TYPE_HAS_MUTABLE_P (type))
and only in that case it sets
          DECL_DECLARED_CONSTEXPR_P (var) = true;
          DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var) = true;
          TREE_CONSTANT (var) = true;
          TREE_READONLY (var) = true;
etc.

So, two questions, which compiler is right and does the expansion stmt
wording need to change?

        Jakub

Reply via email to