bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: nullptr in C++ mode on macOS


From: Arsen Arsenović
Subject: Re: nullptr in C++ mode on macOS
Date: Thu, 09 Feb 2023 21:08:36 +0100

Hi,

Bruno Haible <bruno@clisp.org> writes:

> On macOS 12.5 (machine: gcc104.fsffrance.org), the test-nullptr-c++.cc fails
> to compile:
>
>  -----------------------------------------------------------------------------
> Making check in .
> c++ -DHAVE_CONFIG_H -I. -I../../gltests -I..  -DGNULIB_STRICT_CHECKING=1 
> -DIN_GNULIB_TESTS=1 -I. -I../../gltests -I.. -I../../gltests/.. -I../gllib 
> -I../../gltests/../gllib -I/Users/haible/include -Wall  -Wno-error -g -O2 -MT 
> test-nullptr-c++.o -MD -MP -MF .deps/test-nullptr-c++.Tpo -c -o 
> test-nullptr-c++.o ../../gltests/test-nullptr-c++.cc
> ../../gltests/test-nullptr-c++.cc:56:27: error: cannot pass object of non-POD 
> type 'std::nullptr_t' through variadic function; call will abort at runtime 
> [-Wnon-pod-varargs]
>   varargs_callee ("type", nullptr, "foo", nullptr);
>                           ^
> /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__nullptr:48:17:
>  note: expanded from macro 'nullptr'
> #define nullptr _VSTD::__get_nullptr_t()
>                 ^
> /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__config:858:15:
>  note: expanded from macro '_VSTD'
> #define _VSTD std::_LIBCPP_ABI_NAMESPACE
>               ^
> ../../gltests/test-nullptr-c++.cc:56:43: error: cannot pass object of non-POD 
> type 'std::nullptr_t' through variadic function; call will abort at runtime 
> [-Wnon-pod-varargs]
>   varargs_callee ("type", nullptr, "foo", nullptr);
>                                           ^
> /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__nullptr:48:17:
>  note: expanded from macro 'nullptr'
> #define nullptr _VSTD::__get_nullptr_t()
>                 ^
> /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__config:858:15:
>  note: expanded from macro '_VSTD'
> #define _VSTD std::_LIBCPP_ABI_NAMESPACE
>               ^
> 2 errors generated.
> make[3]: *** [test-nullptr-c++.o] Error 1
>  -----------------------------------------------------------------------------
>
> What's happening? configure found out that
>
>   checking for C nullptr... no
>   checking for C++ nullptr... no
>
> and accordingly, config.h contains
>
> /* #undef HAVE_CXX_NULLPTR */
> /* #undef HAVE_C_NULLPTR */
>
> Here, nullptr is not defined as a keyword (as it should, cf.
> https://en.cppreference.com/w/cpp/keyword/nullptr), but rather as a macro,
> and only after some header file such as <stddef.h> has been included.
>
> Our test "checking for C++ nullptr... no" found it out correctly.
>
> But test-nullptr-c++.cc includes <iostream>, <vector>, etc. and these have
> the effect of pulling a definition of nullptr_t and
>
>   #define nullptr _VSTD::__get_nullptr_t()
>
> and this one does not work in varargs context.
>
> So, we've now gone a full circle:
>   * nullptr and nullptr_t were introduced in C++, to resolve a problem with
>     overloading and a problem with varargs.
>   * The definition in this compiler solves the problem with overloading,
>     but is unusable with varargs.
>   * If we '#define nullptr __null' again after including the header files,
>     it will be usable with varargs, but will
>       - violate the requirement that nullptr is an instance of nullptr_t,
>       - and thus open up problems with overloading again.

The latter problem avoided as long as nullptr is not convertible to
integrals, which is doable.

N2431 proposes a possible base for an alternative:

  const                        // this is a const object...
  class {
  public:
    template<class T>          // convertible to any type
      operator T*() const      // of null non-member
      { return 0; }            // pointer...
    template<class C, class T> // or any type of null
      operator T C::*() const  // member pointer...
      { return 0; }
    void* x = 0;               // added by me, to pretend this works in
                               // variadics
  private:
    void operator&() const;    // whose address can't be taken
  } nlptr = {};                // and whose name is nullptr

  #define nullptr nlptr

  /* This builds on Clang++ 11, which is the closes approx. for AC++ I
     have.  */
  void g(...){}
  void test() {
      g(nullptr);
  }

The paper outlines a few drawbacks to this approach, but it's better
than what libc++ was offering AFAICT.  Please test it on Apple Clang
(I'm failing to come up with the correct set of compiler sources to
inspect this, and failing to emulate the failure with Clang 10, which
appears to be matching macOS 12.5s llvm version, except not quite).

To get the layout of this object to roughly match that of a null
pointer, for sake of variadic passing, I added the ``x''.

I'm not sure why the libc++ folk thought to poorly emulate a later
language feature without calling it __nullptr or such.

Note that this broken libc++ nullptr might be only enabled in pre-11 C++
modes based on what I've seen in llvm-project.git.  If that's the case,
I suspect the alternative proposed above was intentionally not chosen in
order to avoid some other bug I'm failing to see at this point in the
night.  Whether pre-11 standards are worth supporting is up to you, but
my vote is on treating it like C99, though C++11 was more substantial
than C99, and requiring it to be enabled.  I urge this option, or not
reusing the nullptr name.

It's, to my awareness, not possible to correctly and portably implement
nullptr in-language.  I'll ask some friends for second opinions too.

> I don't know how to proceed here. C++ is such a waste of time!!

This is a fault of an implementation, or an error in usage.

> Bruno

Hope that helps, have a great night.
-- 
Arsen Arsenović

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]