Skip to content

[BUG] UFCS fails during constant evaluation only #788

Open

Description

@JohelEGP

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.

Activity

  1. JohelEGP commented on Oct 29, 2023

    @JohelEGP
    ContributorAuthor

    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.

  2. JohelEGP commented on Oct 29, 2023

    @JohelEGP
    ContributorAuthor

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

  3. JohelEGP commented on Dec 3, 2023

    @JohelEGP
    ContributorAuthor

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

  4. JohelEGP commented on Sep 30, 2024

    @JohelEGP
    ContributorAuthor

    (Now?) g(0) also works with GCC (https://cpp2.godbolt.org/z/8v13hM44h).
    I'd expect Clang to work, by choosing the member, since that works, before even trying the object.
    Maybe there's a Clang bug lurking,
    or some constant expression quirk where Clang is strict and GCC is loose.
    It's worth debugging the UFCS macro to see exactly what triggers Clang for further investigation.

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