cppfront icon indicating copy to clipboard operation
cppfront copied to clipboard

[BUG] UFCS fails during constant evaluation only

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

Title: UFCS fails during constant evaluation only.

Description:

Given a parameter named after the function being called, UFCS will fail during constant evaluation only in the case UFCS would call a member function.

Minimal reproducer (https://cpp2.godbolt.org/z/jKWG4boaq, https://compiler-explorer.com/z/zeqW6MEPK):

t: @struct type = {
  f: (this) -> int == 0;
}

g: (f) = {
  [[assert: t().f() == 0]] // OK.
  static_assert(t().f() == 0); // Error: Argument `f` isn't "`constexpr`".
}

main: () = {
  g(0);
  // g(std::identity()); // Might be OK since P2280R4 (unimplemented).
}
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 that calls the member function.

Actual result and error:

Cpp2 lowered to Cpp1:


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


#include "cpp2util.h"

class t;
  

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

class t {
  public: [[nodiscard]] constexpr auto f() const& -> int;
};

auto g(auto const& f) -> void;
  

auto main() -> int;
  

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


  [[nodiscard]] constexpr auto t::f() const& -> int { return 0;  }

auto g(auto const& f) -> void{
  cpp2::Default.expects(CPP2_UFCS_0(f, t()) == 0, "");// OK.
  static_assert(CPP2_UFCS_0(f, t()) == 0);// Error: Argument `f` isn't "`constexpr`".
}

auto main() -> int{
  g(0);
  // g(std::identity()); // Might be OK since P2280R4 (unimplemented).
}
Output:
main.cpp2:7:17: error: static assertion expression is not an integral constant expression
    7 |   static_assert(CPP2_UFCS_0(f, t()) == 0);// Error: Argument `f` isn't "`constexpr`".
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:790:38: note: expanded from macro 'CPP2_UFCS_0'
  790 | #define CPP2_UFCS_0(FUNCNAME,PARAM1) \
      |                                      ^
main.cpp2:11:3: note: in instantiation of function template specialization 'g<int>' requested here
   11 |   g(0);
      |   ^
main.cpp2:7:17: note: function parameter 'f' with unknown value cannot be used in a constant expression
    7 |   static_assert(CPP2_UFCS_0(f, t()) == 0);// Error: Argument `f` isn't "`constexpr`".
      |                 ^
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:791:2: note: expanded from macro 'CPP2_UFCS_0'
  791 | [&] CPP2_LAMBDA_NO_DISCARD (auto&& obj) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
      |  ^
main.cpp2:5:20: note: declared here
    5 | auto g(auto const& f) -> void{
      |                    ^
1 error generated.

JohelEGP avatar Oct 29 '23 01:10 JohelEGP

My reading of https://eel.is/c++draft/expr.const#8 tells me that g(0); will continue failing whereas g(std::identity()); would be well-formed.

#506's resolution for #550 adds quite some complexity to Cppfront. The same can be done here to resolve this #788. The alternative is to just embrace #550 and #788 as limitations of Cppfront, and require users to not name such variables like the function in a UFCS call.

JohelEGP avatar Oct 29 '23 14:10 JohelEGP

I have potential solutions in mind for this one. But they also bring complexity to the code of Cppfront.

Maybe trial constant evaluation can be leveraged for a seamless user experience. An alternative would be to disable UFCS when in a constant expression (so Cppfront has to know about those) and f isn't part of the constant expression (determined by lexical name lookup).

JohelEGP avatar Oct 29 '23 15:10 JohelEGP

// g(std::identity()); // Might be OK since P2280R4 (unimplemented). Yes, it works: https://cpp2.godbolt.org/z/1vrf534qG.

JohelEGP avatar Dec 03 '23 19:12 JohelEGP