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.133.124]) by sourceware.org (Postfix) with ESMTPS id 060913858C2F for ; Mon, 15 Aug 2022 20:03:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 060913858C2F Received: from mail-qv1-f69.google.com (mail-qv1-f69.google.com [209.85.219.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-44-i03U0jH0OqC5-8v56tSj0g-1; Mon, 15 Aug 2022 16:03:16 -0400 X-MC-Unique: i03U0jH0OqC5-8v56tSj0g-1 Received: by mail-qv1-f69.google.com with SMTP id nk7-20020a056214350700b0047688bd2105so3119419qvb.16 for ; Mon, 15 Aug 2022 13:03:16 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:in-reply-to:from:references:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc; bh=SAtf41M6EZd0cVA0vuimi/wB7lecSQBAYYMc3z1mMTg=; b=7+jvyV9ynKMUQxV/xsn5dbyyKq6clJT0sUIOthPqc6BwvKQxJvrM6I7/Roj7P12trX uNKQXTC9J2RvmE1kMM1fQ7BtWvbbobuKnClkeRkbD6ty98pYHXHw3hUjxiSPWRCm/hFt MYvH8blKlGLTM2TTvFls8b2J6kqpNwA7x856KfIAEUBJD889Uiqbq8aYY9oxoyF6ZeXo xaWAblIO4/C7rOt4ixuucHxaLYhUK1RdE76YXyehLKBlPzsY3TQ1ETwC18IMy176QFXK cxc8e3LwXRXVJ/armdEdXjlRD/YssqW8ZrEPN5D+q5z7+94RhlcN9ayQIM4km0FpVS4+ sAtA== X-Gm-Message-State: ACgBeo2VkVW6M+6xPWgO9HO4R+uyFiNEf8o7jutsIZi0Y/zp4l+3kuA5 05HDdX+LzNZ4vQlsziGygAo3o437UHJ4FplK0bPixpSO22xpHKZEKhJvKFbuAbXqsk0BMwS7ANi E49enyQKsy3KjYJbe9A== X-Received: by 2002:a05:620a:1b8f:b0:6bb:3bb2:a88a with SMTP id dv15-20020a05620a1b8f00b006bb3bb2a88amr3483154qkb.37.1660593795622; Mon, 15 Aug 2022 13:03:15 -0700 (PDT) X-Google-Smtp-Source: AA6agR76mSTYsDwEOg0RY2r5R5MIXsGRJ4NpTnYWWO02QgaHLRwMmexa5ZUKuwCh9+wdkZR/PEOfdg== X-Received: by 2002:a05:620a:1b8f:b0:6bb:3bb2:a88a with SMTP id dv15-20020a05620a1b8f00b006bb3bb2a88amr3483085qkb.37.1660593794608; Mon, 15 Aug 2022 13:03:14 -0700 (PDT) Received: from [192.168.1.101] (130-44-159-43.s15913.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.159.43]) by smtp.gmail.com with ESMTPSA id h4-20020a05620a244400b006b5a12eb838sm4884254qkn.31.2022.08.15.13.03.13 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 15 Aug 2022 13:03:14 -0700 (PDT) Message-ID: <38054995-48c3-8e98-623a-c2057ec70fb3@redhat.com> Date: Mon, 15 Aug 2022 16:03:13 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.12.0 Subject: Re: [PATCH] c: Implement C23 nullptr (N3042) To: Marek Polacek , GCC Patches , Joseph Myers References: <20220813213504.568937-1-polacek@redhat.com> From: Jason Merrill In-Reply-To: <20220813213504.568937-1-polacek@redhat.com> X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, NICE_REPLY_A, RCVD_IN_DNSWL_NONE, 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 X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 15 Aug 2022 20:03:24 -0000 On 8/13/22 14:35, Marek Polacek wrote: > This patch implements the C23 nullptr literal: > , 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 . I'm uncertain about > the __STDC_VERSION__ version I should be checking. Also, I'm not > defining __STDC_VERSION_STDDEF_H__ yet, because I don't know what value > it should be defined to. Do we know yet? > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? The C++ changes are OK, but you probably want a comment in c_common_nodes_and_builtins that we aren't setting the alignment there for C++ backward ABI bug compatibility. Or perhaps set it there and then break it in the C++ front end when abi < 9. > gcc/c-family/ChangeLog: > > * c-common.cc (c_common_reswords): Enable nullptr in C. > (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. > (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_parser_postfix_expression): Handle RID_NULLPTR. > * c-typeck.cc (null_pointer_constant_p): Return true for NULLPTR_TYPE_P. > (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. > : Likewise. > > 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/Wcxx-compat-2.c: Remove nullptr 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 | 13 +- > gcc/c-family/c-common.h | 8 + > gcc/c-family/c-pretty-print.cc | 7 + > gcc/c/c-convert.cc | 19 ++- > gcc/c/c-decl.cc | 6 + > gcc/c/c-parser.cc | 8 + > gcc/c/c-typeck.cc | 55 +++++- > gcc/cp/cp-tree.h | 8 - > gcc/cp/decl.cc | 8 +- > gcc/ginclude/stddef.h | 8 + > gcc/testsuite/gcc.dg/Wcxx-compat-2.c | 1 - > gcc/testsuite/gcc.dg/c2x-nullptr-1.c | 239 +++++++++++++++++++++++++++ > gcc/testsuite/gcc.dg/c2x-nullptr-2.c | 9 + > gcc/testsuite/gcc.dg/c2x-nullptr-3.c | 62 +++++++ > gcc/testsuite/gcc.dg/c2x-nullptr-4.c | 10 ++ > gcc/testsuite/gcc.dg/c2x-nullptr-5.c | 11 ++ > 16 files changed, 448 insertions(+), 24 deletions(-) > 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..809e7ff5804 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -500,7 +500,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_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 +4723,17 @@ 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. */ > + 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..d30174334c2 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, > @@ -534,6 +536,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 +1014,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..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; > + } > + gcc_fallthrough (); > case REFERENCE_TYPE: > ret = convert_to_pointer (type, e); > goto maybe_fold; > @@ -180,7 +188,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 ae8990c138f..ac4394c9dc5 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..2af3a614fb9 100644 > --- a/gcc/c/c-parser.cc > +++ b/gcc/c/c-parser.cc > @@ -10243,6 +10243,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 d37de2a313b..2ba48345a1b 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 (NULLPTR_TYPE_P (type)) > + 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,9 +7627,10 @@ 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, > + /* An explicit constant 0 or nullptr can convert to a pointer, > or one that results from arithmetic, even including > a cast to integer type. */ > if (!null_pointer_constant) > @@ -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,31 @@ build_binary_op (location_t location, enum tree_code code, > result_type = type1; > pedwarn (location, 0, "comparison between pointer and integer"); > } > + else if (null_pointer_constant_p (orig_op0) > + && null_pointer_constant_p (orig_op1)) > + { > + /* 6.5.9: One of the following shall hold: > + -- both operands have type nullptr_t; */ > + 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) > + result_type = (INTEGRAL_TYPE_P (type1) > + ? build_pointer_type (type1) : type1); > + else > + 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 3278b4114bd..eb461bf8374 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 ff56fddba54..ba4fb21d36a 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..2ccf411ed88 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__ >= 202000L) > +#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/Wcxx-compat-2.c b/gcc/testsuite/gcc.dg/Wcxx-compat-2.c > index 4578bece109..61d33a0b028 100644 > --- a/gcc/testsuite/gcc.dg/Wcxx-compat-2.c > +++ b/gcc/testsuite/gcc.dg/Wcxx-compat-2.c > @@ -18,7 +18,6 @@ int friend; /* { dg-warning "5:keyword" } */ > int mutable; /* { dg-warning "5:keyword" } */ > int namespace; /* { dg-warning "5:keyword" } */ > int new; /* { dg-warning "5:keyword" } */ > -int nullptr; /* { dg-warning "5:keyword" } */ > int operator; /* { dg-warning "5:keyword" } */ > int private; /* { dg-warning "5:keyword" } */ > int protected; /* { dg-warning "5:keyword" } */ > 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..49e7031c4d9 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2x-nullptr-1.c > @@ -0,0 +1,239 @@ > +/* Test basic 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 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; > +} > + > +/* 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; > + > + r |= nullptr != nullptr; > + r |= cmp () != nullptr; > + r |= nullptr != cmp (); > + r |= !(nullptr == nullptr); > + r |= !(cmp () == nullptr); > + r |= !(nullptr == cmp ()); > + r |= nullptr != (void *) 0; > + r |= !(nullptr == (void *) 0); > + r |= (void *) 0 != nullptr; > + r |= !((void *) 0 == nullptr); > + r |= nullptr != 0; > + r |= 0 != nullptr; > + r |= !(nullptr == 0); > + r |= !(0 == nullptr); > + r |= nullptr != 0u; > + r |= 0u != nullptr; > + r |= !(nullptr == 0u); > + r |= !(0u == nullptr); > + > + r |= nptr != nptr; > + r |= cmp () != nptr; > + r |= nptr != cmp (); > + r |= !(nptr == nptr); > + r |= !(cmp () == nptr); > + r |= !(nptr == cmp ()); > + 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 (); > + > + (void) (p == nullptr); > + (void) (p != nullptr); > + (void) (nullptr == p); > + (void) (nullptr != p); > +} > + > +/* 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 +/* { dg-do compile } */ > +/* { dg-options "-std=c2x -pedantic-errors" } */ > + > +#include > + > +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..3f37c9b17ed > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2x-nullptr-3.c > @@ -0,0 +1,62 @@ > +/* 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." } */ > + > +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" } */ > +} > + > +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..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" } */ > + > +int * > +fn (int *p) > +{ > + p = nullptr; /* { dg-error "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..7479ab4ea1d > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/c2x-nullptr-5.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; > +} > > base-commit: 4991e20923b658ce9fbdf5621cab39f71b98fbc2