From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 4C15C385E038 for ; Wed, 24 Aug 2022 18:24:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 4C15C385E038 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1661365450; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=PrZz/1el3rAFnYqRELsBgCoZYpWdd/HbjXyR5oK9yXw=; b=B/6h1YmAOjJ0bTAGDPVe/M1iH3a9zpO2betnWRsQqES+A7migjV4Z1M/bGYbJoiL2Cr9/j nwY4yjJLV/RuXoZY+vpRycsJa9cUGJBPzxfKEdOmzkJHTOHgaZuutgYODk5BiOgp2LqKW6 JcB2CDiBTHmQ2shftDCAWF0iq7V+dqY= Received: from mail-qk1-f198.google.com (mail-qk1-f198.google.com [209.85.222.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-407-GpjF4UYDNhuFkPEFHRUnnw-1; Wed, 24 Aug 2022 14:24:08 -0400 X-MC-Unique: GpjF4UYDNhuFkPEFHRUnnw-1 Received: by mail-qk1-f198.google.com with SMTP id w17-20020a05620a425100b006b97978c693so15428328qko.19 for ; Wed, 24 Aug 2022 11:24:05 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:cc:to:from:date:x-gm-message-state:from:to:cc; bh=PrZz/1el3rAFnYqRELsBgCoZYpWdd/HbjXyR5oK9yXw=; b=Z+8py6cuubzeujp4zJtHqZkNYwu2vB/XHSQIIqz2Zc4Mt1QPh+mKCbANUNcFAE0J9X dsZjqVT4B4b17yf8K/xrLeSNkqCJVRy3bpwTeILPUDoTFQsC7BhSoW5SSpF+6Y3y1Lpc +jSbkMeXX8af+ZfMTC7pJ7jpOISVVRisvUANpSLvxM13l5l1nVNzZrH00L+EVZ/JbQs/ 0OggXnufPw+XdaaGUDDvC2O8An8VX0OlTHtSOxa4HoAJ7ydXThIhvhs6gXEpZu93aIPE lOvryFHK9qQ8ZIdPsGIb7vMbCD+RESxY/uszbAh4mWf3Ht3KpPuZ3acUdRGEQElukzWX CsNw== X-Gm-Message-State: ACgBeo1moQQnEm1n5VETqXdVffjkiZ7w2Zd3XaCHVBXO6NWkRfYS5IDe riixGVudZt0RCl5HSLZbDPR5m3PrHGz75b7tpGHJnTfwsotnCk6qJpOi45/bMMw69jhL3pVUzFK P+dUIjsPCx8PiWRimgA== X-Received: by 2002:ac8:5804:0:b0:344:5ba8:81e0 with SMTP id g4-20020ac85804000000b003445ba881e0mr486047qtg.181.1661365443296; Wed, 24 Aug 2022 11:24:03 -0700 (PDT) X-Google-Smtp-Source: AA6agR5B9AlsV48KgHTJKjV/9kW8PE0F8Mtclms7nV7xNkFDTqZ3pitC0V1g8R0u5a5VhKJD8gfa6Q== X-Received: by 2002:ac8:5804:0:b0:344:5ba8:81e0 with SMTP id g4-20020ac85804000000b003445ba881e0mr486001qtg.181.1661365442500; Wed, 24 Aug 2022 11:24:02 -0700 (PDT) Received: from redhat.com ([2601:184:4780:4310::e531]) by smtp.gmail.com with ESMTPSA id h5-20020a05620a400500b006bbb07ebd83sm17166159qko.108.2022.08.24.11.24.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Aug 2022 11:24:01 -0700 (PDT) Date: Wed, 24 Aug 2022 14:24:00 -0400 From: Marek Polacek To: Joseph Myers Cc: GCC Patches Subject: [PATCH v2] c: Implement C23 nullptr (N3042) Message-ID: References: <20220813213504.568937-1-polacek@redhat.com> MIME-Version: 1.0 In-Reply-To: User-Agent: Mutt/2.2.6 (2022-06-05) X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=us-ascii Content-Disposition: inline X-Spam-Status: No, score=-12.9 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_LOW,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Mon, Aug 15, 2022 at 05:48:34PM +0000, Joseph Myers wrote: > On Sat, 13 Aug 2022, Marek Polacek via Gcc-patches wrote: > > > This patch also defines nullptr_t in . I'm uncertain about > > the __STDC_VERSION__ version I should be checking. Also, I'm not > > We're using defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L until > the final version for C23 is settled. OK, adjusted. > > defining __STDC_VERSION_STDDEF_H__ yet, because I don't know what value > > it should be defined to. Do we know yet? > > No, and Jens's comments on the editorial review before CD ballot include > that lots of headers don't yet have such a macro definition but should > have one, as well as needing consistency for the numbers. > > We won't know the final values for these macros until much later, because > the timescale depends on whether ISO decides to delay things at any point > by coming up with a long list of editorial issues required to follow the > JTC1 Directives as they did for C17 (objections to particular words > appearing in non-normative text, etc.). Ack, thanks. > > - { "nullptr", RID_NULLPTR, D_CXXONLY | D_CXX11 | D_CXXWARN }, > > + { "nullptr", RID_NULLPTR, D_CXX11 | D_CXXWARN }, > > You need to use D_C2X (which doesn't yet exist). In pre-C23 modes, > nullptr needs to be a normal identifier that can be used in all contexts > where identifiers can be used, not a keyword at all (and then you need a > c11-nullptr*.c test to verify that use of it as an identifier works as > expected). Fixed. Adding D_C2X meant that I had to enlarge struct c_common_resword by a word. > > diff --git a/gcc/c/c-convert.cc b/gcc/c/c-convert.cc > > index 18083d59618..013fe6b2a53 100644 > > --- a/gcc/c/c-convert.cc > > +++ b/gcc/c/c-convert.cc > > @@ -133,6 +133,14 @@ c_convert (tree type, tree expr, bool init_const) > > (loc, type, c_objc_common_truthvalue_conversion (input_location, expr)); > > > > case POINTER_TYPE: > > + /* The type nullptr_t may be converted to a pointer type. The result is > > + a null pointer value. */ > > + if (NULLPTR_TYPE_P (TREE_TYPE (e))) > > + { > > + ret = build_int_cst (type, 0); > > + goto maybe_fold; > > + } > > That looks like it would lose side-effects. You need to preserve > side-effects in an expression of nullptr_t type being converted to a > pointer type, and need an execution testcase that verifies such > side-effects are preserved. Ah, of course. Fixed by building up a COMPOUND_EXPR. Covered by c2x-nullptr-5.c. > Also, you need to make sure that (void *)nullptr is not treated as a null > pointer constant, only a null pointer; build_int_cst (type, 0) would > produce a null pointer constant when type is void *. (void *)nullptr > should be handled similarly to (void *)(void *)0, which isn't a null > pointer constant either. That (void *)nullptr is not considered an NPC ought to be achieved by build_c_cast wrapping the result of c_convert in a NOP_EXPR: /* Don't allow the results of casting to floating-point or complex types be confused with actual constants, or casts involving integer and pointer types other than direct integer-to-integer and integer-to-pointer be confused with integer constant expressions and null pointer constants. */ Then null_pointer_constant_p sees the NOP_EXPR and returns false. I've added a comment explaining that. > > @@ -133,6 +133,13 @@ null_pointer_constant_p (const_tree expr) > > /* This should really operate on c_expr structures, but they aren't > > yet available everywhere required. */ > > tree type = TREE_TYPE (expr); > > + > > + /* An integer constant expression with the value 0, such an expression > > + cast to type void*, or the predefined constant nullptr, are a null > > + pointer constant. */ > > + if (NULLPTR_TYPE_P (type)) > > + return true; > > That looks wrong. You need to distinguish null pointer constants of type > nullptr_t (nullptr, possibly enclosed in parentheses, possibly the > selected alternative from _Generic) from all other expressions of type > nullptr_t (including (nullptr_t)nullptr, which isn't a null pointer > constant any more than (void *)(void *)0). Ah, okay. I had just copied what we do in C++ in null_ptr_cst_p and the rest of the patch worked under that assumption. I've added some tests for this too. Except I don't really understand the _Generic comment so I only have tests for _Generic that were in the previous version. Changing null_pointer_constant_p mean that I had to adjust build_binary_op/EQ_EXPR. > Then, for each context where it matters whether a nullptr_t value is a > null pointer constant, there need to be testcases that the two cases are > properly distinguished. This includes at least equality comparisons with > a pointer that is not a null pointer constant (seem only to be allowed > with nullptr, not with other nullptr_t expressions). (I think for > conditional expressions, conditionals between nullptr_t and an integer > null pointer constant are always invalid, whether or not the nullptr_t is > a null pointer constant, while conditionals between nullptr_t and a > pointer are always valid.) I see, it's because 6.5.9 says that if one of the operands of == or != has type nullptr_t but the other one doesn't, the other operand has to be an NPC, not just a null pointer. This should be tested in c2x-nullptr-1.c:test2 and c2x-nullptr-3.c:test1. For instance, "p == (nullptr_t)nullptr", where p is a pointer, does not compile. > > +/* The type nullptr_t shall not be converted to any type other than bool or > > + a pointer type. No type other than nullptr_t shall be converted to nullptr_t. */ > > That's other than *void*, bool or a pointer type. (That's a correct fix > to the N3042 wording in N3047. There are other problems in the > integration of nullptr in N3047 that are only fixed in my subsequent fixes > as part of the editorial review - and many issues with integration of > other papers that haven't yet been fixed, I currently have 25 open merge > requests resulting from editorial review.) And of course conversions from > nullptr_t to void should be tested. Thanks, tests added to c2x-nullptr-1.c:test1. I notice that 6.3.2.4 still says "The type nullptr_t may be converted to bool or to a pointer type"; isn't it missing the ", void" here too? > > diff --git a/gcc/testsuite/gcc.dg/c2x-nullptr-4.c b/gcc/testsuite/gcc.dg/c2x-nullptr-4.c > > new file mode 100644 > > index 00000000000..5b15e75d159 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/c2x-nullptr-4.c > > @@ -0,0 +1,10 @@ > > +/* Test that we warn about `nullptr' pre-C2X. */ > > +/* { dg-do compile } */ > > +/* { dg-options "-std=c17 -pedantic-errors" } */ > > This test is wrong - it's a normal identifier pre-C2x - but tests for > previous standard versions shouldn't be called c2x-* in any case. I see. I've removed this test and instead added c17-nullptr-2.c. Thank you for the careful review. Bootstrap/regtest running on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- This patch implements the C23 nullptr literal: (with wording fixes from N3047), which is intended to replace the problematic definition of NULL which might be either of integer type or void*. Since C++ has had nullptr for over a decade now, it was relatively easy to just move the built-in node definitions from the C++ FE to the C/C++ common code. Also, our DWARF emitter already handles NULLPTR_TYPE by emitting DW_TAG_unspecified_type. However, I had to handle a lot of contexts such as ?:, comparison, conversion, etc. There are some minor differences, e.g. in C you can do bool b = nullptr; but in C++ you have to use direct-initialization: bool b{nullptr}; And I think that nullptr_t n = 0; is only valid in C++. Of course, C doesn't have to handle mangling, RTTI, substitution, overloading, ... This patch also defines nullptr_t in . However, it does not define __STDC_VERSION_STDDEF_H__ yet, because we don't know yet what value it should be defined to. gcc/c-family/ChangeLog: * c-common.cc (c_common_reswords): Enable nullptr in C2X. (c_common_nodes_and_builtins): Create the built-in node for nullptr. * c-common.h (enum c_tree_index): Add CTI_NULLPTR, CTI_NULLPTR_TYPE. (struct c_common_resword): Resize the disable member. (D_C2X): Add. (nullptr_node): Define. (nullptr_type_node): Define. (NULLPTR_TYPE_P): Define. * c-pretty-print.cc (c_pretty_printer::simple_type_specifier): Handle NULLPTR_TYPE. (c_pretty_printer::direct_abstract_declarator): Likewise. (c_pretty_printer::constant): Likewise. gcc/c/ChangeLog: * c-convert.cc (c_convert) : Handle NULLPTR_TYPE. Give a better diagnostic when converting to nullptr_t. * c-decl.cc (c_init_decl_processing): Perform C-specific nullptr initialization. * c-parser.cc (c_parse_init): Maybe OR D_C2X into mask. (c_parser_postfix_expression): Handle RID_NULLPTR. * c-typeck.cc (null_pointer_constant_p): Return true when expr is nullptr_node. (build_unary_op) : Handle NULLPTR_TYPE. (build_conditional_expr): Handle the case when the second/third operand is NULLPTR_TYPE and third/second operand is POINTER_TYPE. (convert_for_assignment): Handle converting an expression of type nullptr_t to pointer/bool. (build_binary_op) : Handle NULLPTR_TYPE. : Handle comparing operands of type nullptr_t. gcc/cp/ChangeLog: * cp-tree.h (enum cp_tree_index): Remove CTI_NULLPTR, CTI_NULLPTR_TYPE. Move it to c_tree_index. (nullptr_node): No longer define here. (nullptr_type_node): Likewise. (NULLPTR_TYPE_P): Likewise. * decl.cc (cxx_init_decl_processing): Only keep C++-specific nullptr initialization; move the shared code to c_common_nodes_and_builtins. gcc/ChangeLog: * ginclude/stddef.h: Define nullptr_t. gcc/testsuite/ChangeLog: * gcc.dg/c11-nullptr-1.c: New test. * gcc.dg/c17-nullptr-1.c: New test. * gcc.dg/c17-nullptr-2.c: New test. * gcc.dg/c2x-nullptr-1.c: New test. * gcc.dg/c2x-nullptr-2.c: New test. * gcc.dg/c2x-nullptr-3.c: New test. * gcc.dg/c2x-nullptr-4.c: New test. * gcc.dg/c2x-nullptr-5.c: New test. --- gcc/c-family/c-common.cc | 20 ++- gcc/c-family/c-common.h | 37 ++-- gcc/c-family/c-pretty-print.cc | 7 + gcc/c/c-convert.cc | 25 ++- gcc/c/c-decl.cc | 6 + gcc/c/c-parser.cc | 10 ++ gcc/c/c-typeck.cc | 57 +++++- gcc/cp/cp-tree.h | 8 - gcc/cp/decl.cc | 8 +- gcc/ginclude/stddef.h | 8 + gcc/testsuite/gcc.dg/c11-nullptr-1.c | 10 ++ gcc/testsuite/gcc.dg/c17-nullptr-1.c | 10 ++ gcc/testsuite/gcc.dg/c17-nullptr-2.c | 10 ++ gcc/testsuite/gcc.dg/c2x-nullptr-1.c | 259 +++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/c2x-nullptr-2.c | 9 + gcc/testsuite/gcc.dg/c2x-nullptr-3.c | 78 ++++++++ gcc/testsuite/gcc.dg/c2x-nullptr-4.c | 11 ++ gcc/testsuite/gcc.dg/c2x-nullptr-5.c | 14 ++ 18 files changed, 545 insertions(+), 42 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/c11-nullptr-1.c create mode 100644 gcc/testsuite/gcc.dg/c17-nullptr-1.c create mode 100644 gcc/testsuite/gcc.dg/c17-nullptr-2.c create mode 100644 gcc/testsuite/gcc.dg/c2x-nullptr-1.c create mode 100644 gcc/testsuite/gcc.dg/c2x-nullptr-2.c create mode 100644 gcc/testsuite/gcc.dg/c2x-nullptr-3.c create mode 100644 gcc/testsuite/gcc.dg/c2x-nullptr-4.c create mode 100644 gcc/testsuite/gcc.dg/c2x-nullptr-5.c diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index 6e41ceb38e9..82ebe7c4502 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -324,8 +324,10 @@ static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT); if they match the mask. Masks for languages: - C --std=c89: D_C99 | D_CXXONLY | D_OBJC | D_CXX_OBJC - C --std=c99: D_CXXONLY | D_OBJC + C --std=c89: D_C99 | D_C2X | D_CXXONLY | D_OBJC | D_CXX_OBJC + C --std=c99: D_C2X | D_CXXONLY | D_OBJC + C --std=c17: D_C2X | D_CXXONLY | D_OBJC + C --std=c2x: D_CXXONLY | D_OBJC ObjC is like C except that D_OBJC and D_CXX_OBJC are not set C++ --std=c++98: D_CONLY | D_CXX11 | D_CXX20 | D_OBJC C++ --std=c++11: D_CONLY | D_CXX20 | D_OBJC @@ -500,7 +502,7 @@ const struct c_common_resword c_common_reswords[] = { "namespace", RID_NAMESPACE, D_CXXONLY | D_CXXWARN }, { "new", RID_NEW, D_CXXONLY | D_CXXWARN }, { "noexcept", RID_NOEXCEPT, D_CXXONLY | D_CXX11 | D_CXXWARN }, - { "nullptr", RID_NULLPTR, D_CXXONLY | D_CXX11 | D_CXXWARN }, + { "nullptr", RID_NULLPTR, D_C2X | D_CXX11 | D_CXXWARN }, { "operator", RID_OPERATOR, D_CXXONLY | D_CXXWARN }, { "private", RID_PRIVATE, D_CXX_OBJC | D_CXXWARN }, { "protected", RID_PROTECTED, D_CXX_OBJC | D_CXXWARN }, @@ -4723,6 +4725,18 @@ c_common_nodes_and_builtins (void) null_node = make_int_cst (1, 1); TREE_TYPE (null_node) = c_common_type_for_size (POINTER_SIZE, 0); + /* Create the built-in nullptr node. This part of its initialization is + common to C and C++. The front ends can further adjust its definition + in {c,cxx}_init_decl_processing. In particular, we aren't setting the + alignment here for C++ backward ABI bug compatibility. */ + nullptr_type_node = make_node (NULLPTR_TYPE); + TYPE_SIZE (nullptr_type_node) = bitsize_int (GET_MODE_BITSIZE (ptr_mode)); + TYPE_SIZE_UNIT (nullptr_type_node) = size_int (GET_MODE_SIZE (ptr_mode)); + TYPE_UNSIGNED (nullptr_type_node) = 1; + TYPE_PRECISION (nullptr_type_node) = GET_MODE_BITSIZE (ptr_mode); + SET_TYPE_MODE (nullptr_type_node, ptr_mode); + nullptr_node = build_int_cst (nullptr_type_node, 0); + /* Since builtin_types isn't gc'ed, don't export these nodes. */ memset (builtin_types, 0, sizeof (builtin_types)); } diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index c06769b6f0b..e7b0fd1309d 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -375,6 +375,8 @@ enum c_tree_index CTI_DEFAULT_FUNCTION_TYPE, CTI_NULL, + CTI_NULLPTR, + CTI_NULLPTR_TYPE, /* These are not types, but we have to look them up all the time. */ CTI_FUNCTION_NAME_DECL, @@ -409,7 +411,7 @@ struct c_common_resword { const char *const word; ENUM_BITFIELD(rid) const rid : 16; - const unsigned int disable : 16; + const unsigned int disable : 32; }; /* Mode used to build pointers (VOIDmode means ptr_mode). */ @@ -447,19 +449,20 @@ extern machine_mode c_default_pointer_mode; #define D_CONLY 0x0001 /* C only (not in C++). */ #define D_CXXONLY 0x0002 /* C++ only (not in C). */ #define D_C99 0x0004 /* In C, C99 only. */ -#define D_CXX11 0x0008 /* In C++, C++11 only. */ -#define D_EXT 0x0010 /* GCC extension. */ -#define D_EXT89 0x0020 /* GCC extension incorporated in C99. */ -#define D_ASM 0x0040 /* Disabled by -fno-asm. */ -#define D_OBJC 0x0080 /* In Objective C and neither C nor C++. */ -#define D_CXX_OBJC 0x0100 /* In Objective C, and C++, but not C. */ -#define D_CXXWARN 0x0200 /* In C warn with -Wcxx-compat. */ -#define D_CXX_CONCEPTS 0x0400 /* In C++, only with concepts. */ -#define D_TRANSMEM 0X0800 /* C++ transactional memory TS. */ -#define D_CXX_CHAR8_T 0X1000 /* In C++, only with -fchar8_t. */ -#define D_CXX20 0x2000 /* In C++, C++20 only. */ -#define D_CXX_COROUTINES 0x4000 /* In C++, only with coroutines. */ -#define D_CXX_MODULES 0x8000 /* In C++, only with modules. */ +#define D_C2X 0x0008 /* In C, C2X only. */ +#define D_CXX11 0x0010 /* In C++, C++11 only. */ +#define D_EXT 0x0020 /* GCC extension. */ +#define D_EXT89 0x0040 /* GCC extension incorporated in C99. */ +#define D_ASM 0x0080 /* Disabled by -fno-asm. */ +#define D_OBJC 0x0100 /* In Objective C and neither C nor C++. */ +#define D_CXX_OBJC 0x0200 /* In Objective C, and C++, but not C. */ +#define D_CXXWARN 0x0400 /* In C warn with -Wcxx-compat. */ +#define D_CXX_CONCEPTS 0x0800 /* In C++, only with concepts. */ +#define D_TRANSMEM 0x1000 /* C++ transactional memory TS. */ +#define D_CXX_CHAR8_T 0x2000 /* In C++, only with -fchar8_t. */ +#define D_CXX20 0x4000 /* In C++, C++20 only. */ +#define D_CXX_COROUTINES 0x8000 /* In C++, only with coroutines. */ +#define D_CXX_MODULES 0x10000 /* In C++, only with modules. */ #define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS #define D_CXX_CHAR8_T_FLAGS D_CXXONLY | D_CXX_CHAR8_T @@ -534,6 +537,9 @@ extern const unsigned int num_c_common_reswords; /* The node for C++ `__null'. */ #define null_node c_global_trees[CTI_NULL] +/* The nodes for `nullptr'. */ +#define nullptr_node c_global_trees[CTI_NULLPTR] +#define nullptr_type_node c_global_trees[CTI_NULLPTR_TYPE] extern GTY(()) tree c_global_trees[CTI_MAX]; @@ -1009,6 +1015,9 @@ extern void c_parse_final_cleanups (void); #define DECL_UNNAMED_BIT_FIELD(NODE) \ (DECL_C_BIT_FIELD (NODE) && !DECL_NAME (NODE)) +/* True iff TYPE is cv decltype(nullptr). */ +#define NULLPTR_TYPE_P(TYPE) (TREE_CODE (TYPE) == NULLPTR_TYPE) + extern tree do_case (location_t, tree, tree); extern tree build_stmt (location_t, enum tree_code, ...); extern tree build_real_imag_expr (location_t, enum tree_code, tree); diff --git a/gcc/c-family/c-pretty-print.cc b/gcc/c-family/c-pretty-print.cc index 71a0cb51093..efa1768f4d6 100644 --- a/gcc/c-family/c-pretty-print.cc +++ b/gcc/c-family/c-pretty-print.cc @@ -321,6 +321,7 @@ pp_c_pointer (c_pretty_printer *pp, tree t) _Bool -- C99 _Complex -- C99 _Imaginary -- C99 + nullptr_t -- C23 struct-or-union-specifier enum-specifier typedef-name. @@ -424,6 +425,9 @@ c_pretty_printer::simple_type_specifier (tree t) else translate_string (""); break; + case NULLPTR_TYPE: + pp_c_ws_string (this, "nullptr_t"); + break; default: pp_unsupported_tree (this, t); @@ -678,6 +682,7 @@ c_pretty_printer::direct_abstract_declarator (tree t) case COMPLEX_TYPE: case TYPE_DECL: case ERROR_MARK: + case NULLPTR_TYPE: break; default: @@ -1219,6 +1224,8 @@ c_pretty_printer::constant (tree e) pp_c_character_constant (this, e); else if (TREE_CODE (type) == ENUMERAL_TYPE) pp_c_enumeration_constant (this, e); + else if (NULLPTR_TYPE_P (type)) + pp_string (this, "nullptr"); else pp_c_integer_constant (this, e); } diff --git a/gcc/c/c-convert.cc b/gcc/c/c-convert.cc index 18083d59618..6e7491339d4 100644 --- a/gcc/c/c-convert.cc +++ b/gcc/c/c-convert.cc @@ -133,6 +133,20 @@ c_convert (tree type, tree expr, bool init_const) (loc, type, c_objc_common_truthvalue_conversion (input_location, expr)); case POINTER_TYPE: + /* The type nullptr_t may be converted to a pointer type. The result is + a null pointer value. */ + if (NULLPTR_TYPE_P (TREE_TYPE (e))) + { + /* To make sure that (void *)nullptr is not a null pointer constant, + build_c_cast will create an additional NOP_EXPR around the result + of this conversion. */ + if (TREE_SIDE_EFFECTS (e)) + ret = build2 (COMPOUND_EXPR, type, e, build_int_cst (type, 0)); + else + ret = build_int_cst (type, 0); + goto maybe_fold; + } + gcc_fallthrough (); case REFERENCE_TYPE: ret = convert_to_pointer (type, e); goto maybe_fold; @@ -180,7 +194,16 @@ c_convert (tree type, tree expr, bool init_const) return ret; } - error ("conversion to non-scalar type requested"); + /* If we are converting to nullptr_t, don't say "non-scalar type" because + the nullptr_t type is a scalar type. Only nullptr_t shall be converted + to nullptr_t. */ + if (code == NULLPTR_TYPE) + { + error ("conversion from %qT to %qT", TREE_TYPE (e), type); + inform (input_location, "only %qT can be converted to %qT", type, type); + } + else + error ("conversion to non-scalar type requested"); return error_mark_node; } diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 9e590c66dae..cbba0c62f64 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -4531,6 +4531,12 @@ c_init_decl_processing (void) pushdecl (build_decl (UNKNOWN_LOCATION, TYPE_DECL, get_identifier ("_Bool"), boolean_type_node)); + /* C-specific nullptr initialization. */ + record_builtin_type (RID_MAX, "nullptr_t", nullptr_type_node); + /* The size and alignment of nullptr_t is the same as for a pointer to + character type. */ + SET_TYPE_ALIGN (nullptr_type_node, GET_MODE_ALIGNMENT (ptr_mode)); + input_location = save_loc; make_fname_decl = c_make_fname_decl; diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 759f200a7eb..8520fd0e2a8 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -119,6 +119,8 @@ c_parse_init (void) mask |= D_CXXONLY; if (!flag_isoc99) mask |= D_C99; + if (!flag_isoc2x) + mask |= D_C2X; if (flag_no_asm) { mask |= D_ASM | D_EXT; @@ -10243,6 +10245,14 @@ c_parser_postfix_expression (c_parser *parser) "% clause"); expr.set_error (); break; + /* C23 'nullptr' literal. */ + case RID_NULLPTR: + c_parser_consume_token (parser); + expr.value = nullptr_node; + set_c_expr_source_range (&expr, tok_range); + pedwarn_c11 (loc, OPT_Wpedantic, + "ISO C does not support %qs before C2X", "nullptr"); + break; default: c_parser_error (parser, "expected expression"); expr.set_error (); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index de8780a1502..3b5275b6e0a 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -133,6 +133,13 @@ null_pointer_constant_p (const_tree expr) /* This should really operate on c_expr structures, but they aren't yet available everywhere required. */ tree type = TREE_TYPE (expr); + + /* An integer constant expression with the value 0, such an expression + cast to type void*, or the predefined constant nullptr, are a null + pointer constant. */ + if (expr == nullptr_node) + return true; + return (TREE_CODE (expr) == INTEGER_CST && !TREE_OVERFLOW (expr) && integer_zerop (expr) @@ -4575,7 +4582,7 @@ build_unary_op (location_t location, enum tree_code code, tree xarg, case TRUTH_NOT_EXPR: if (typecode != INTEGER_TYPE && typecode != FIXED_POINT_TYPE && typecode != REAL_TYPE && typecode != POINTER_TYPE - && typecode != COMPLEX_TYPE) + && typecode != COMPLEX_TYPE && typecode != NULLPTR_TYPE) { error_at (location, "wrong type argument to unary exclamation mark"); @@ -5515,6 +5522,13 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, } result_type = type2; } + /* 6.5.15: "if one is a null pointer constant (other than a pointer) or has + type nullptr_t and the other is a pointer, the result type is the pointer + type." */ + else if (code1 == NULLPTR_TYPE && code2 == POINTER_TYPE) + result_type = type2; + else if (code1 == POINTER_TYPE && code2 == NULLPTR_TYPE) + result_type = type1; if (!result_type) { @@ -7613,12 +7627,13 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, error_at (location, msg); return error_mark_node; } - else if (codel == POINTER_TYPE && coder == INTEGER_TYPE) + else if (codel == POINTER_TYPE + && (coder == INTEGER_TYPE || coder == NULLPTR_TYPE)) { - /* An explicit constant 0 can convert to a pointer, - or one that results from arithmetic, even including - a cast to integer type. */ - if (!null_pointer_constant) + /* An explicit constant 0 or type nullptr_t can convert to a pointer, + or one that results from arithmetic, even including a cast to + integer type. */ + if (!null_pointer_constant && coder != NULLPTR_TYPE) switch (errtype) { case ic_argpass: @@ -7691,7 +7706,10 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, return convert (type, rhs); } - else if (codel == BOOLEAN_TYPE && coder == POINTER_TYPE) + else if (codel == BOOLEAN_TYPE + /* The type nullptr_t may be converted to bool. The + result is false. */ + && (coder == POINTER_TYPE || coder == NULLPTR_TYPE)) { tree ret; bool save = in_late_binary_op; @@ -12107,10 +12125,10 @@ build_binary_op (location_t location, enum tree_code code, case TRUTH_XOR_EXPR: if ((code0 == INTEGER_TYPE || code0 == POINTER_TYPE || code0 == REAL_TYPE || code0 == COMPLEX_TYPE - || code0 == FIXED_POINT_TYPE) + || code0 == FIXED_POINT_TYPE || code0 == NULLPTR_TYPE) && (code1 == INTEGER_TYPE || code1 == POINTER_TYPE || code1 == REAL_TYPE || code1 == COMPLEX_TYPE - || code1 == FIXED_POINT_TYPE)) + || code1 == FIXED_POINT_TYPE || code1 == NULLPTR_TYPE)) { /* Result of these operations is always an int, but that does not mean the operands should be @@ -12418,6 +12436,27 @@ build_binary_op (location_t location, enum tree_code code, result_type = type1; pedwarn (location, 0, "comparison between pointer and integer"); } + /* 6.5.9: One of the following shall hold: + -- both operands have type nullptr_t; */ + else if (code0 == NULLPTR_TYPE && code1 == NULLPTR_TYPE) + { + result_type = nullptr_type_node; + /* No need to convert the operands to result_type later. */ + converted = 1; + } + /* -- one operand has type nullptr_t and the other is a null pointer + constant. We will have to convert the former to the type of the + latter, because during gimplification we can't have mismatching + comparison operand type. We convert from nullptr_t to the other + type, since only nullptr_t can be converted to nullptr_t. Also, + even a constant 0 is a null pointer constant, so we may have to + create a pointer type from its type. */ + else if (code0 == NULLPTR_TYPE && null_pointer_constant_p (orig_op1)) + result_type = (INTEGRAL_TYPE_P (type1) + ? build_pointer_type (type1) : type1); + else if (code1 == NULLPTR_TYPE && null_pointer_constant_p (orig_op0)) + result_type = (INTEGRAL_TYPE_P (type0) + ? build_pointer_type (type0) : type0); if ((TREE_CODE (TREE_TYPE (orig_op0)) == BOOLEAN_TYPE || truth_value_p (TREE_CODE (orig_op0))) ^ (TREE_CODE (TREE_TYPE (orig_op1)) == BOOLEAN_TYPE diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 9f2ff3728b4..c897da204fe 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -187,9 +187,6 @@ enum cp_tree_index CPTI_NOEXCEPT_FALSE_SPEC, CPTI_NOEXCEPT_DEFERRED_SPEC, - CPTI_NULLPTR, - CPTI_NULLPTR_TYPE, - CPTI_ANY_TARG, CPTI_MODULE_HWM, @@ -254,8 +251,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; #define conv_op_marker cp_global_trees[CPTI_CONV_OP_MARKER] #define abort_fndecl cp_global_trees[CPTI_ABORT_FNDECL] #define current_aggr cp_global_trees[CPTI_AGGR_TAG] -#define nullptr_node cp_global_trees[CPTI_NULLPTR] -#define nullptr_type_node cp_global_trees[CPTI_NULLPTR_TYPE] /* std::align_val_t */ #define align_type_node cp_global_trees[CPTI_ALIGN_TYPE] @@ -4405,9 +4400,6 @@ get_vec_init_expr (tree t) || TREE_CODE (TYPE) == REAL_TYPE \ || TREE_CODE (TYPE) == COMPLEX_TYPE) -/* True iff TYPE is cv decltype(nullptr). */ -#define NULLPTR_TYPE_P(TYPE) (TREE_CODE (TYPE) == NULLPTR_TYPE) - /* [basic.types] Arithmetic types, enumeration types, pointer types, diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 84a1a011341..d46a347a6c7 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -4793,16 +4793,10 @@ cxx_init_decl_processing (void) } } - nullptr_type_node = make_node (NULLPTR_TYPE); - TYPE_SIZE (nullptr_type_node) = bitsize_int (GET_MODE_BITSIZE (ptr_mode)); - TYPE_SIZE_UNIT (nullptr_type_node) = size_int (GET_MODE_SIZE (ptr_mode)); - TYPE_UNSIGNED (nullptr_type_node) = 1; - TYPE_PRECISION (nullptr_type_node) = GET_MODE_BITSIZE (ptr_mode); + /* C++-specific nullptr initialization. */ if (abi_version_at_least (9)) SET_TYPE_ALIGN (nullptr_type_node, GET_MODE_ALIGNMENT (ptr_mode)); - SET_TYPE_MODE (nullptr_type_node, ptr_mode); record_builtin_type (RID_MAX, "decltype(nullptr)", nullptr_type_node); - nullptr_node = build_int_cst (nullptr_type_node, 0); } if (! supports_one_only ()) diff --git a/gcc/ginclude/stddef.h b/gcc/ginclude/stddef.h index 79e296d4a66..315ff786694 100644 --- a/gcc/ginclude/stddef.h +++ b/gcc/ginclude/stddef.h @@ -443,6 +443,14 @@ typedef struct { #endif #endif /* C++11. */ +#if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) +#ifndef _GCC_NULLPTR_T +#define _GCC_NULLPTR_T + typedef __typeof__(nullptr) nullptr_t; +/* ??? This doesn't define __STDC_VERSION_STDDEF_H__ yet. */ +#endif +#endif /* C23. */ + #endif /* _STDDEF_H was defined this time */ #endif /* !_STDDEF_H && !_STDDEF_H_ && !_ANSI_STDDEF_H && !__STDDEF_H__ diff --git a/gcc/testsuite/gcc.dg/c11-nullptr-1.c b/gcc/testsuite/gcc.dg/c11-nullptr-1.c new file mode 100644 index 00000000000..c4faedc2c91 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-nullptr-1.c @@ -0,0 +1,10 @@ +/* Test that in pre-C23 modes, nullptr is a normal identifier, + not a keyword. */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +int nullptr; + +void +f (int nullptr) +{ +} diff --git a/gcc/testsuite/gcc.dg/c17-nullptr-1.c b/gcc/testsuite/gcc.dg/c17-nullptr-1.c new file mode 100644 index 00000000000..92e43b9df23 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c17-nullptr-1.c @@ -0,0 +1,10 @@ +/* Test that in pre-C23 modes, nullptr is a normal identifier, + not a keyword. */ +/* { dg-options "-std=c17 -pedantic-errors" } */ + +int nullptr; + +void +f (int nullptr) +{ +} diff --git a/gcc/testsuite/gcc.dg/c17-nullptr-2.c b/gcc/testsuite/gcc.dg/c17-nullptr-2.c new file mode 100644 index 00000000000..a6ad7703eeb --- /dev/null +++ b/gcc/testsuite/gcc.dg/c17-nullptr-2.c @@ -0,0 +1,10 @@ +/* Test that we don't predefine `nullptr' pre-C2X. */ +/* { dg-do compile } */ +/* { dg-options "-std=c17 -pedantic-errors" } */ + +int * +fn (int *p) +{ + p = nullptr; /* { dg-error "'nullptr' undeclared" } */ + return p; +} diff --git a/gcc/testsuite/gcc.dg/c2x-nullptr-1.c b/gcc/testsuite/gcc.dg/c2x-nullptr-1.c new file mode 100644 index 00000000000..ca01b3e3296 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-nullptr-1.c @@ -0,0 +1,259 @@ +/* Test valid usage of C23 nullptr. */ +/* { dg-do run } */ +/* { dg-options "-std=c2x -pedantic-errors -Wall -Wextra -Wno-unused-variable" } */ + +#include + +typedef __typeof__(nullptr) nullptr_t; + +void f1 (nullptr_t) { } +void f2 (int *) { } +void f3 (_Bool) { } +nullptr_t cmp (void) { return nullptr; } + +/* The type nullptr_t shall not be converted to any type other than void, bool or + a pointer type. No type other than nullptr_t shall be converted to nullptr_t. */ +void +test1 (void) +{ + const nullptr_t nptr = nullptr; + static nullptr_t static_nptr; + int *p1 = nullptr; + void *p2 = nullptr; + float *p3 = nullptr; + void (*p4)(int) = nullptr; + int (*p5)[10] = nullptr; + int *p6 = nptr; + void *p7 = nptr; + float *p8 = nptr; + void (*p9)(int) = nptr; + int (*p10)[10] = nptr; + int *p11 = (int *) nullptr; + int *p12 = (int *) nptr; + if (nullptr || p1 || p2 || p3 || p4 || p5 || p6 || p7 || p8 || p9 || p10 + || p11 || p12) + __builtin_abort (); + + _Bool b1 = nullptr; + _Bool b2 = (_Bool) nullptr; + _Bool b3 = nptr; + _Bool b4 = (_Bool) nptr; + if (b1 || b2 || b3 || b4 || (_Bool) nullptr || (_Bool) nptr) + __builtin_abort (); + + __auto_type a1 = nullptr; + __auto_type a2 = nptr; + + /* We can convert nullptr_t to nullptr_t. */ + __typeof__(nullptr) x = nullptr; + f1 (x); + f1 (nullptr); + f2 (x); + f2 (nullptr); + f3 (nullptr); + + const nullptr_t np1 = nullptr; + const nullptr_t np2 = np1; + (void) nullptr; + (void) np1; + (void) np2; + (void) cmp (); + (void)(nullptr_t) nullptr; +} + +/* Test valid comparison. */ +void +test2 (int *p) +{ + /* If both operands have type nullptr_t or one operand has type nullptr_t + and the other is a null pointer constant, they compare equal. */ + const nullptr_t nptr = nullptr; + int r = 0; + + /* Both operands have type nullptr_t. */ + r |= nullptr != nullptr; + r |= cmp () != nullptr; + r |= nullptr != cmp (); + r |= !(nullptr == nullptr); + r |= !(cmp () == nullptr); + r |= !(nullptr == cmp ()); + r |= nptr != nptr; + r |= cmp () != nptr; + r |= nptr != cmp (); + r |= !(nptr == nptr); + r |= !(cmp () == nptr); + r |= !(nptr == cmp ()); + + /* One operand has type nullptr_t and the other is a null pointer constant. */ + r |= nullptr != (void *) 0; + r |= (nullptr) != (void *) 0; + r |= !(nullptr == (void *) 0); + r |= (void *) 0 != nullptr; + r |= (void *) 0 != (nullptr); + r |= !((void *) 0 == nullptr); + r |= nullptr != 0; + r |= (nullptr) != 0; + r |= 0 != nullptr; + r |= 0 != (nullptr); + r |= !(nullptr == 0); + r |= !(0 == nullptr); + r |= nullptr != 0u; + r |= 0u != nullptr; + r |= !(nullptr == 0u); + r |= !(0u == nullptr); + r |= nptr != (void *) 0; + r |= !(nptr == (void *) 0); + r |= (void *) 0 != nptr; + r |= !((void *) 0 == nptr); + r |= nptr != 0; + r |= 0 != nptr; + r |= !(nptr == 0); + r |= !(0 == nptr); + r |= nptr != 0u; + r |= 0u != nptr; + r |= !(nptr == 0u); + r |= !(0u == nptr); + if (r) + __builtin_abort (); + + /* One operand is a pointer and the other is a null pointer constant. */ + (void) (p == nullptr); + (void) (p != nullptr); + (void) (nullptr == p); + (void) (nullptr != p); + (void) (p == (nullptr)); + (void) (p != (nullptr)); + (void) ((nullptr) == p); + (void) ((nullptr) != p); + (void) ((void *)nullptr == nullptr); + (void) ((void *)nullptr != nullptr); + (void) (nullptr == (void *)nullptr); + (void) (nullptr != (void *)nullptr); +} + +/* Test ?:. */ +void +test3 (int *p, _Bool b) +{ + int x = nullptr ? 1 : 2; + (void) x; + const nullptr_t nptr = nullptr; + /* One of the following shall hold for the second and third operands: + -- both operands have nullptr_t type. */ + __auto_type r1 = b ? nullptr : nullptr; + __auto_type r2 = b ? nptr : nptr; + /* -- one operand is a pointer and the other is a null pointer constant + or has type nullptr_t; */ + __auto_type r3 = b ? p : nullptr; + __auto_type r4 = b ? nullptr : p; + __auto_type r5 = b ? nptr : p; + __auto_type r6 = b ? p : nptr; + __auto_type r7 = b ? 0 : p; + __auto_type r8 = b ? p : 0; + __auto_type r9 = b ? p : cmp (); + __auto_type r10 = b ? cmp () : p; +} + +/* Simple assignment. */ +void +test4 (void) +{ + /* -- the left operand has an atomic, qualified, or unqualified version of + the nullptr_t type and the type of the right is nullptr_t; */ + nullptr_t n1; + n1 = nullptr; + const nullptr_t n2 = nullptr; + _Atomic nullptr_t n3 = nullptr; + volatile nullptr_t n4 = nullptr; + /* -- the left operand is an atomic, qualified, or unqualified pointer, + and the type of the right is nullptr_t; */ + int *p1 = cmp (); + _Atomic int *p2 = cmp (); + const int *volatile p3 = cmp (); + const int *const *const p4 = cmp (); + double (*const p5)(void) = n1; + /* -- the left operand is an atomic, qualified, or unqualified bool, and + the type of the right is nullptr_t; */ + _Bool b1; + b1 = cmp (); + const _Bool b2 = nullptr; + _Atomic _Bool b3; + b3 = n1; + (void) b1; + (void) b3; +} + +/* var_arg etc. */ +static void +test5 (int i, ...) +{ + va_list ap; + va_start (ap, i); + if (va_arg (ap, void *)) + __builtin_abort (); +} + +/* Operand of alignas, sizeof or typeof operators. */ +void +test6 (void) +{ + _Static_assert (sizeof (nullptr) == sizeof (void *), "sizeof (nullptr)"); + _Static_assert (sizeof (nullptr_t) == sizeof (void *), "sizeof (nullptr_t)"); + _Static_assert (sizeof (nullptr) == sizeof (char *), "sizeof (nullptr)"); + _Static_assert (sizeof (nullptr_t) == sizeof (char *), "sizeof (nullptr_t)"); + _Static_assert (_Alignof (nullptr_t) == _Alignof (char *), "_Alignof (nullptr_t)"); + __typeof__(nullptr) t = nullptr; + f1 (t); + _Alignas (nullptr_t) char i1 = 'q'; + + _Static_assert (_Generic (nullptr, nullptr_t: 1, default: 0) == 1, "_Generic"); + _Static_assert (_Generic (t, nullptr_t: 1, default: 0) == 1, "_Generic"); + _Static_assert (_Generic (cmp (), nullptr_t: 1, default: 0) == 1, "_Generic"); + _Static_assert (_Generic (0, nullptr_t: 1, int: 2, default: 0) == 2, "_Generic"); + _Static_assert (_Generic ((void *)0, nullptr_t: 1, void *: 2, default: 0) == 2, "_Generic"); + _Static_assert (_Generic (nullptr, nullptr_t: 1, void *: 2, default: 0) == 1, "_Generic"); +} + +/* Play with !, ||, &&. */ +void +test7 (void) +{ + if (nullptr) + __builtin_abort (); + if (1 && nullptr) + __builtin_abort (); + if (0 || nullptr) + __builtin_abort (); + if (nullptr && 1) + __builtin_abort (); + if (nullptr || 0) + __builtin_abort (); + if (!nullptr) + { + } + else + __builtin_abort (); + while (nullptr) + __builtin_abort (); + int i = 0; + do + ++i; + while (nullptr); + if (i != 1) + __builtin_abort (); + for (;nullptr;) + __builtin_abort (); +} + +int +main (void) +{ + int i = 42; + test1 (); + test2 (&i); + test3 (&i, 0); + test4 (); + test5 (42, nullptr); + test6 (); + test7 (); +} diff --git a/gcc/testsuite/gcc.dg/c2x-nullptr-2.c b/gcc/testsuite/gcc.dg/c2x-nullptr-2.c new file mode 100644 index 00000000000..2cc88353581 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-nullptr-2.c @@ -0,0 +1,9 @@ +/* Test nullptr_t from + +void f(nullptr_t); +_Static_assert (sizeof (nullptr_t) == sizeof (char *), "sizeof (nullptr_t)"); +_Static_assert (_Alignof (nullptr_t) == _Alignof (char *), "_Alignof (nullptr_t)"); diff --git a/gcc/testsuite/gcc.dg/c2x-nullptr-3.c b/gcc/testsuite/gcc.dg/c2x-nullptr-3.c new file mode 100644 index 00000000000..91e9b1eb883 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-nullptr-3.c @@ -0,0 +1,78 @@ +/* Test wrong usage of C23 nullptr. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors -Wall -Wextra -Wno-unused-variable" } */ + +typedef __typeof__(nullptr) nullptr_t; + +void g (nullptr_t); /* { dg-message "expected .nullptr_t. but argument is of type .int." } */ +nullptr_t cmp (void); + +void +test1 (int *p) +{ + (void) (p > nullptr); /* { dg-error "ordered comparison" } */ + (void) (p >= nullptr); /* { dg-error "ordered comparison" } */ + (void) (p < nullptr); /* { dg-error "ordered comparison" } */ + (void) (p <= nullptr); /* { dg-error "ordered comparison" } */ + (void) (nullptr == 1); /* { dg-error "invalid operands" } */ + (void) (1 == nullptr); /* { dg-error "invalid operands" } */ + (void) (nullptr != 1); /* { dg-error "invalid operands" } */ + (void) (1 != nullptr); /* { dg-error "invalid operands" } */ + (void) (1 > nullptr); /* { dg-error "invalid operands" } */ + + /* "(nullptr_t)nullptr" has type nullptr_t but isn't an NPC. */ + (void) ((nullptr_t)nullptr == p); /* { dg-error "invalid operands" } */ + (void) ((nullptr_t)nullptr != p); /* { dg-error "invalid operands" } */ + (void) (p == (nullptr_t)nullptr); /* { dg-error "invalid operands" } */ + (void) (p != (nullptr_t)nullptr); /* { dg-error "invalid operands" } */ + (void) (cmp () == p); /* { dg-error "invalid operands" } */ + (void) (cmp () != p); /* { dg-error "invalid operands" } */ + (void) (p == cmp ()); /* { dg-error "invalid operands" } */ + (void) (p != cmp ()); /* { dg-error "invalid operands" } */ + /* "(void *)nullptr" is not an NPC, either. */ + (void) ((void *)nullptr == cmp ()); /* { dg-error "invalid operands" } */ + (void) ((void *)nullptr != cmp ()); /* { dg-error "invalid operands" } */ + (void) (cmp () == (void *)nullptr); /* { dg-error "invalid operands" } */ + (void) (cmp () != (void *)nullptr); /* { dg-error "invalid operands" } */ +} + +void +test2 (void) +{ + const nullptr_t nptr = nullptr; + int p = nullptr; /* { dg-error "incompatible types" } */ + float d = nullptr; /* { dg-error "incompatible types" } */ + char arr[10] = { nullptr }; /* { dg-error "incompatible types" } */ + + /* No type other than nullptr_t shall be converted to nullptr_t. */ + const nullptr_t n = 0; /* { dg-error "invalid initializer" } */ + +(nullptr_t) 0; /* { dg-error "conversion from .int. to .nullptr_t." } */ + + g (0); /* { dg-error "incompatible type" } */ + + int i = 42 + nullptr; /* { dg-error "invalid operands" } */ + + /* The assignment of an object of type nullptr_t with a value of another + type, even if the value is a null pointer constant, is a constraint + violation. */ + nullptr_t m; + m = 0; /* { dg-error "incompatible types" } */ + (void) m; + nullptr_t o = 0; /* { dg-error "invalid initializer" } */ + + switch (nullptr); /* { dg-error "switch quantity not an integer" } */ +} + +/* If a second or third operand of type nullptr_t is used that is not a null + pointer constant and the other operand is not a pointer or does not have + itself nullptr_t, a constraint is violated even if that other operand is + a null pointer constant such as 0. */ +void +test3 (_Bool b, int i) +{ + const nullptr_t nptr = nullptr; + __auto_type a1 = b ? nptr : i; /* { dg-error "type mismatch" } */ + __auto_type a2 = b ? i : nptr; /* { dg-error "type mismatch" } */ + __auto_type a3 = b ? nptr : 0; /* { dg-error "type mismatch" } */ + __auto_type a4 = b ? 0 : nptr; /* { dg-error "type mismatch" } */ +} diff --git a/gcc/testsuite/gcc.dg/c2x-nullptr-4.c b/gcc/testsuite/gcc.dg/c2x-nullptr-4.c new file mode 100644 index 00000000000..7479ab4ea1d --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-nullptr-4.c @@ -0,0 +1,11 @@ +/* Test that -Wc11-c2x-compat issues a warning (not a pedwarn) about + `nullptr' in C2X. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -pedantic-errors -Wc11-c2x-compat" } */ + +int * +fn (int *p) +{ + p = nullptr; /* { dg-warning "ISO C does not support .nullptr. before C2X" } */ + return p; +} diff --git a/gcc/testsuite/gcc.dg/c2x-nullptr-5.c b/gcc/testsuite/gcc.dg/c2x-nullptr-5.c new file mode 100644 index 00000000000..27803f7d03f --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-nullptr-5.c @@ -0,0 +1,14 @@ +/* Test that we don't lose side-effects when converting from nullptr_t. */ +/* { dg-do run } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +int i; +nullptr_t fn () { ++i; return nullptr; } + +int +main () +{ + int *p = fn (); + if (i != 1) + __builtin_abort (); +} base-commit: 530f80451a9e76896a0294e0f4bd59baff1ac27f -- 2.37.2