Skip to content

[libc++] stddef.h needs to #include_next for the new clang __need_ macros #86252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions libcxx/include/stddef.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,28 @@
//
//===----------------------------------------------------------------------===//

#if defined(__need_ptrdiff_t) || defined(__need_size_t) || defined(__need_wchar_t) || defined(__need_NULL) || \
defined(__need_wint_t)
#if defined(__need_ptrdiff_t) || defined(__need_size_t) || defined(__need_rsize_t) || defined(__need_wchar_t) || \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would actually suggest that we change the whole header to this:

// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

/*
    stddef.h synopsis

Macros:

    offsetof(type,member-designator)
    NULL

Types:

    ptrdiff_t
    size_t
    max_align_t // C++11
    nullptr_t

*/

#include <__config>

// Note: This include is outside of header guards because we sometimes get included multiple times
//       with different defines and the underlying <stddef.h> will know how to deal with that.
#include_next <stddef.h>

#ifndef _LIBCPP_STDDEF_H
#  define _LIBCPP_STDDEF_H

#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#    pragma GCC system_header
#  endif

#  ifdef __cplusplus
typedef decltype(nullptr) nullptr_t;
#  endif

#endif // _LIBCPP_STDDEF_H

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can unconditionally declare nullptr_t. If e.g. __need_size_t is set, then nullptr_t shouldn't get declared. I'm not sure if that's for POSIX conformance or what, but it seems to be important when I talk to people implementing low level headers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see what the problem would be though? We'd be defining it at most once due to the header guard, and nobody else anywhere should be defining nullptr_t anyways.

Either way, I can try doing that in a separate patch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I officially don't understand this patch anymore so I would argue we should go for the simpler approach suggested above unless there's a concrete reason why that wouldn't work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that some headers need to declare just size_t, and it's a problem if anything else gets declared. The way they do that is by setting __need_size_t and including stddef.h. I think it's for POSIX reasons but I'm not 100% why it's so important for people to be able to only see pieces of stdarg and stddef.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AaronBallman do you know why the __need_ macros are so important in the clang stddef.h?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that this is needed for interfacing with other CRTs (primarily Apple's?):
https://opensource.apple.com/source/Libc/Libc-825.26/include/stddef.h.auto.html
https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20151005/140394.html

and was carried forward with newer functionality more recently:
https://reviews.llvm.org/D157757

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for digging this up. We're checking with some folks internally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The answer we're getting from the Darwin folks is that they can't find a historical reason for it. Basically our current understanding is that on Darwin at least, the most naive implementation (without any __need_FOO macros) should work. Or at least it would be intended to work.

I don't know about Linuxes though. At the very least, as far as libc++ is concerned, I am quite tempted to close this PR and go with #86843 instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No one's really sure why it's important for the __need_ macros to be perfectly strict. Trying out #86843 internally. We're still talking about if we want to keep this PR for the tests and copy stddef.h from that one, or dump the tests and just do 86843 by itself.

defined(__need_NULL) || defined(__need_nullptr_t) || defined(__need_unreachable) || defined(__need_max_align_t) || \
defined(__need_offsetof) || defined(__need_wint_t)

# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
# endif

# if defined(__need_nullptr_t) && defined(__cplusplus)
// stddef.h will undef __need_nullptr_t
# define __cxx_need_nullptr_t
# endif

# include_next <stddef.h>

#elif !defined(_LIBCPP_STDDEF_H)
# ifdef __cxx_need_nullptr_t
# include <__config>
typedef decltype(nullptr) nullptr_t;
# undef __cxx_need_nullptr_t
# endif

#elif !defined(_LIBCPP_STDDEF_H) || (defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ >= 1)
# define _LIBCPP_STDDEF_H

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// This is the same test as clang/test/Headers/stddef.c, but to test the
// libc++ version of stddef.h interacts properly with the clang version.

struct astruct {
char member;
};

ptrdiff_t p0; // expected-error{{unknown type name 'ptrdiff_t'}}
size_t s0; // expected-error{{unknown type name 'size_t'}}
rsize_t r0; // expected-error{{unknown type name 'rsize_t'}}
wchar_t wc0; // wchar_t is a keyword in C++
void* v0 = NULL; // expected-error{{use of undeclared identifier 'NULL'}}
nullptr_t n0; // expected-error{{unknown type name 'nullptr_t'}}
static void f0(void) { unreachable(); } // expected-error{{undeclared identifier 'unreachable'}}
max_align_t m0; // expected-error{{unknown type name 'max_align_t'}}
size_t o0 = offsetof(struct astruct, member); // expected-error{{unknown type name 'size_t'}}
// expected-error@-1{{expected expression}} expected-error@-1{{use of undeclared identifier 'member'}}
wint_t wi0; // expected-error{{unknown type name 'wint_t'}}

#include <stddef.h>

ptrdiff_t p1;
size_t s1;
rsize_t r1;
#if __has_feature(modules) && (__cplusplus <= 202302L)
// expected-error-re@-2{{declaration of 'rsize_t' must be imported from module '{{.+}}' before it is required}}
#else
// expected-error@-4{{unknown type}} expected-note@__stddef_size_t.h:*{{'size_t' declared here}}
#endif
wchar_t wc1;
void* v1 = NULL;
nullptr_t n1;
// unreachable() is declared in <utility> in C++
// https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2826.pdf suggests that it maybe should also be
// declared in <stddef.h> too, but it currently isn't.
static void f1(void) { unreachable(); } // expected-error 0+ {{undeclared identifier}}
max_align_t m1;
#if __cplusplus < 201103L
// expected-error@-2{{unknown type}}
#endif
size_t o1 = offsetof(struct astruct, member);
wint_t wi1; // expected-error{{unknown type}}

// rsize_t needs to be opted into via __STDC_WANT_LIB_EXT1__ >= 1.
#define __STDC_WANT_LIB_EXT1__ 1
#include <stddef.h>
ptrdiff_t p2;
size_t s2;
rsize_t r2;
wchar_t wc2;
void* v2 = NULL;
nullptr_t n2;
static void f2(void) { unreachable(); } // expected-error 0+ {{undeclared identifier}}
max_align_t m2;
#if __cplusplus < 201103L
// expected-error@-2{{unknown type}}
#endif
size_t o2 = offsetof(struct astruct, member);
wint_t wi2; // expected-error{{unknown type}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// This is the same test as clang/test/Headers/stddefneeds.c, but to test the
// libc++ version of stddef.h interacts properly with the clang version.

struct astruct {
char member;
};

ptrdiff_t p0; // expected-error{{unknown type name 'ptrdiff_t'}}
size_t s0; // expected-error{{unknown type name 'size_t'}}
rsize_t r0; // expected-error{{unknown type name 'rsize_t'}}
wchar_t wc0; // wchar_t is a keyword in C++
void* v0 = NULL; // expected-error{{use of undeclared identifier 'NULL'}}
nullptr_t n0; // expected-error{{unknown type name 'nullptr_t'}}
static void f0(void) { unreachable(); } // expected-error{{undeclared identifier 'unreachable'}}
max_align_t m0; // expected-error{{unknown type name 'max_align_t'}}
size_t o0 = offsetof(struct astruct, member); // expected-error{{unknown type name 'size_t'}}
// expected-error@-1{{expected expression}} expected-error@-1{{use of undeclared identifier 'member'}}
wint_t wi0; // expected-error{{unknown type name 'wint_t'}}

#define __need_ptrdiff_t
#include <stddef.h>

ptrdiff_t p1;
size_t s1;
#if __has_feature(modules) && (__cplusplus <= 202302L)
// expected-error-re@-2{{declaration of 'size_t' must be imported from module '{{.+}}' before it is required}}
#else
// expected-error@-4{{unknown type}}
#endif
rsize_t r1;
#if __has_feature(modules) && (__cplusplus <= 202302L)
// expected-error-re@-2{{declaration of 'rsize_t' must be imported from module '{{.+}}' before it is required}}
#else
// expected-error@-4{{unknown type}}
#endif
wchar_t wc1;
void* v1 = NULL; // expected-error{{undeclared identifier}}
nullptr_t n1; // expected-error{{unknown type}}
static void f1(void) { unreachable(); } // expected-error{{undeclared identifier}}
max_align_t m1;
#if __has_feature(modules) && (__cplusplus <= 202302L)
// expected-error-re@-2{{declaration of 'max_align_t' must be imported from module '{{.+}}' before it is required}}
#else
// expected-error@-4{{unknown type}}
#endif
size_t o1 = offsetof(struct astruct, member);
#if !__has_feature(modules) || (__cplusplus > 202302L)
// expected-error@-2{{unknown type}}
#endif
// expected-error@-4{{expected expression}} expected-error@-4{{undeclared identifier}}
wint_t wi1; // expected-error{{unknown type}}

// The "declaration must be imported" errors are only emitted the first time a
// known-but-not-visible type is seen. At this point the _Builtin_stddef module
// has been built and all of the types tried, so most of the errors won't be
// repeated below in modules. The types still aren't available, just the errors
// aren't repeated. e.g. rsize_t still isn't available, if r1 above got deleted,
// its error would move to r2 below.

#define __need_size_t
#include <stddef.h>

ptrdiff_t p2;
size_t s2;
rsize_t r2;
#if !__has_feature(modules) || (__cplusplus > 202302L)
// expected-error@-2{{unknown type}}
#endif
wchar_t wc2;
void* v2 = NULL; // expected-error{{undeclared identifier}}
nullptr_t n2; // expected-error{{unknown type}}
static void f2(void) { unreachable(); } // expected-error{{undeclared identifier}}
max_align_t m2;
#if !__has_feature(modules) || (__cplusplus > 202302L)
// expected-error@-2{{unknown type}}
#endif
size_t o2 =
offsetof(struct astruct, member); // expected-error{{expected expression}} expected-error{{undeclared identifier}}
wint_t wi2; // expected-error{{unknown type}}

#define __need_rsize_t
#include <stddef.h>

ptrdiff_t p3;
size_t s3;
rsize_t r3;
wchar_t wc3;
void* v3 = NULL; // expected-error{{undeclared identifier}}
nullptr_t n3; // expected-error{{unknown type}}
static void f3(void) { unreachable(); } // expected-error{{undeclared identifier}}
max_align_t m3;
#if !__has_feature(modules) || (__cplusplus > 202302L)
// expected-error@-2{{unknown type}}
#endif
size_t o3 =
offsetof(struct astruct, member); // expected-error{{expected expression}} expected-error{{undeclared identifier}}
wint_t wi3; // expected-error{{unknown type}}

#define __need_wchar_t
#include <stddef.h>

ptrdiff_t p4;
size_t s4;
rsize_t r4;
wchar_t wc4;
void* v4 = NULL; // expected-error{{undeclared identifier}}
nullptr_t n4; // expected-error{{unknown type}}
static void f4(void) { unreachable(); } // expected-error{{undeclared identifier}}
max_align_t m4;
#if !__has_feature(modules) || (__cplusplus > 202302L)
// expected-error@-2{{unknown type}}
#endif
size_t o4 =
offsetof(struct astruct, member); // expected-error{{expected expression}} expected-error{{undeclared identifier}}
wint_t wi4; // expected-error{{unknown type}}

#define __need_NULL
#include <stddef.h>

ptrdiff_t p5;
size_t s5;
rsize_t r5;
wchar_t wc5;
void* v5 = NULL;
nullptr_t n5; // expected-error{{unknown type}}
static void f5(void) { unreachable(); } // expected-error{{undeclared identifier}}
max_align_t m5;
#if !__has_feature(modules) || (__cplusplus > 202302L)
// expected-error@-2{{unknown type}}
#endif
size_t o5 =
offsetof(struct astruct, member); // expected-error{{expected expression}} expected-error{{undeclared identifier}}
wint_t wi5; // expected-error{{unknown type}}

// nullptr_t doesn't get declared before C23 because its definition
// depends on nullptr.
#define __need_nullptr_t
#include <stddef.h>

ptrdiff_t p6;
size_t s6;
rsize_t r6;
wchar_t wc6;
void* v6 = NULL;
nullptr_t n6;
static void f6(void) { unreachable(); } // expected-error{{undeclared identifier}}
max_align_t m6;
#if !__has_feature(modules) || (__cplusplus > 202302L)
// expected-error@-2{{unknown type}}
#endif
size_t o6 =
offsetof(struct astruct, member); // expected-error{{expected expression}} expected-error{{undeclared identifier}}
wint_t wi6; // expected-error{{unknown type}}

#define __need_unreachable
#include <stddef.h>

ptrdiff_t p7;
size_t s7;
rsize_t r7;
wchar_t wc7;
void* v7 = NULL;
nullptr_t n7;
// __need_unreachable currently declares unreachable(), but the C++23 standard only lists unreachable() in <utility>
// so maybe stddef.h shouldn't declare it even with __need_unreachable.
static void f7(void) { unreachable(); } // expected-error 0+ {{undeclared identifier}}
max_align_t m7;
#if !__has_feature(modules) || (__cplusplus > 202302L)
// expected-error@-2{{unknown type}}
#endif
size_t o7 =
offsetof(struct astruct, member); // expected-error{{expected expression}} expected-error{{undeclared identifier}}
wint_t wi7; // expected-error{{unknown type}}

#define __need_max_align_t
#include <stddef.h>

ptrdiff_t p8;
size_t s8;
rsize_t r8;
wchar_t wc8;
void* v8 = NULL;
nullptr_t n8;
static void f8(void) { unreachable(); } // expected-error 0+ {{undeclared identifier}}
max_align_t m8;
size_t o8 =
offsetof(struct astruct, member); // expected-error{{expected expression}} expected-error{{undeclared identifier}}
wint_t wi8; // expected-error{{unknown type}}

#define __need_offsetof
#include <stddef.h>

ptrdiff_t p9;
size_t s9;
rsize_t r9;
nullptr_t n9;
static void f9(void) { unreachable(); } // expected-error 0+ {{undeclared identifier}}
wchar_t wc9;
void* v9 = NULL;
max_align_t m9;
size_t o9 = offsetof(struct astruct, member);
wint_t wi9; // expected-error{{unknown type}}

#define __need_wint_t
#include <stddef.h>

ptrdiff_t p10;
size_t s10;
rsize_t r10;
wchar_t wc10;
void* v10 = NULL;
nullptr_t n10;
static void f10(void) { unreachable(); } // expected-error 0+ {{undeclared identifier}}
max_align_t m10;
size_t o10 = offsetof(struct astruct, member);
wint_t wi10;