c: C2x constexpr
authorJoseph Myers <joseph@codesourcery.com>
Sat, 12 Nov 2022 17:03:21 +0000 (17:03 +0000)
committerJoseph Myers <joseph@codesourcery.com>
Sat, 12 Nov 2022 17:03:21 +0000 (17:03 +0000)
commitb556d1773db7174c71c466d9b3cafc25c7d6c825
treeb8d88b114a9e5eb1e143544124c477da19aaa5f9
parentf232715d15618e91c90eb210e23de10909590944
c: C2x constexpr

Implement C2x constexpr (a feature based on the C++ one but much more
minimal, with only constexpr variables, not functions).

I believe this implementation is fully functional for use of this
feature.  However, there are several things that seem unclear about
the specification that I'll need to raise in NB comments.  There are
also areas where there may be followup bug fixes because the
implementation doesn't reject some more obscure cases that ought to be
rejected: cases where a constexpr initializer for floating type meets
the constraints for a constant expression in initializers but not
those for an arithmetic constant expression (previously we haven't had
to track whether something is an arithmetic constant expression in
detail, unlike with integer constant expressions), and some cases
where a tag or struct or union member gets declared indirectly in the
declaration specifiers or declarator of a constexpr declaration, which
is not permitted (modulo lack of clarity in the specification) for
underspecified declarations in general (the cases of a declaration in
the initializer, or a tagged type being directly declared as a type
specifier, are already detected).

Cases of ambiguity in the specification include:

* Many questions (previously raised in WG14 discussions) over the rule
  about what conversions do or do not involve a change of value that's
  not allowed in a constexpr initializer, that aren't properly
  addressed by the normative text (and where the footnote on the
  subject isn't very clear either, and the examples don't necessarily
  follow from the normative text).  I've made a series of choices
  there, that include disallowing all conversions between real and
  complex types or between binary and decimal floating types in
  constexpr initializers, that might not necessarily agree with how
  things end up getting clarified.

  The dfp.cc change also arises here, to allow quiet NaN initializers
  of one DFP type to be used in a constexpr initializer for another
  DFP type (as is possible for signaling NaNs) by ensuring the result
  of such a conversion is properly marked as canonical (note that most
  of the DFP code doesn't actually do anything with NaN payloads at
  all).

* Various issues with what exactly counts as part of a declaration for
  the purposes of the rule on underspecified declarations not
  declaring any identifiers other than ordinary identifiers (and not
  declaring more than one ordinary identifier, though the latter is
  undefined behavior).  These include cases where the declaration of a
  struct / union / enum type appears inside typeof or alignas in the
  declaration specifiers (the latter also applies with auto), or in
  the declarator (e.g. an array size or in a parameter declaration).
  The issues are similar to those involved in C90 DR#115 and C99 DRs
  #277 and #341; the intent may not be the same in all the different
  cases involved, but it's not clear that the normative wording in the
  various places is sufficient to deduce the differences in intent.

* The wording about producing a compound literal constant using member
  access is present in one place but another place only applies that
  to named constants.

* It's not clear when a structure or union constant (a constexpr
  variable or compound literal with structure or union type, or a
  member with such type extracted by a series of member access
  operations) can itself be used in an initializer (constexpr or
  otherwise).  Based on general wording for initializers not having
  been changed, the working draft might only strictly allow it at
  automatic storage duration (but elsewhere it would be undefined
  behavior, not a constraint violation, so no diagnostic required) -
  since that's the only case mentioned where a single expression of
  structure or union type can be used to initialize an object of such
  a type.  But it definitely seems to be allowed in even constexpr
  initializers at automatic storage duration - and since generally
  constexpr initializers (any storage duration) are *more* constrained
  than ordinary static storage duration initializers, it would seem
  odd for it not to be allowed at static storage duration.

* When you do allow such initializers, it's then not entirely clear
  how the constraint that constexpr pointer initializers must be null
  pointer constants should be applied (given that a constexpr object
  of pointer type is a null pointer but *not* a null pointer
  constant).  My guess would be that a constexpr struct or union
  containing such a field should still be allowed as an initializer,
  but the wording could be read otherwise.

* It also becomes important with constexpr exactly what kind of
  constant expression an implicit zero initializer is; the wording for
  default initialization only really deals with the value of the
  initializer and not what kind of constant it is.  In particular,
  this affects whether {} is a valid constexpr initializer for a
  pointer not of type void *, since the wording only talks about a
  null pointer, not whether it's a null pointer *constant*.  I assumed
  that it should be a null pointer constant in that case.

* It's also not entirely clear whether constexpr can be used in the
  declaration part of a for loop (which "shall only declare
  identifiers for objects having storage class auto or register").  I
  interpreted it as allowed (treating such objects as implicitly auto
  just like those with no storage class specifiers), but it could also
  be argued that constexpr is another storage class specifier and so
  not allowed there.

Bootstrapped with no regressions for x86_64-pc-linux-gnu.

gcc/
* dfp.cc (decimal_from_binary): Convert a canonical NaN to a
canonical NaN.

gcc/c-family/
* c-common.cc (c_common_reswords): Use D_C2X instead of D_CXXONLY.

gcc/c/
* c-decl.cc (start_underspecified_init)
(finish_underspecified_init): Handle name == NULL_TREE for
compound literals.
(merge_decls): Merge C_DECL_DECLARED_CONSTEXPR.
(shadow_tag_warned): Check for constexpr.
(start_decl): Add parameter do_push.
(build_compound_literal): Set C_DECL_DECLARED_CONSTEXPR.
(grokdeclarator): Handle constexpr.
(finish_struct): Set C_TYPE_FIELDS_NON_CONSTEXPR.
(declspecs_add_scspec): Handle constexpr.
* c-parser.cc (c_token_starts_compound_literal)
(c_token_starts_declspecs, c_parser_declaration_or_fndef)
(c_parser_declspecs, c_parser_gnu_attribute_any_word)
(c_parser_compound_literal_scspecs)
(c_parser_postfix_expression_after_paren_type): Handle constexpr.
Update calls to start_init.
(c_parser_declaration_or_fndef, c_parser_initializer)
(c_parser_initval): Pass true for new argument of
convert_lvalue_to_rvalue.  Call convert_lvalue_to_rvalue for
constexpr compound literals.
(c_parser_static_assert_declaration_no_semi)
(c_parser_enum_specifier, c_parser_struct_declaration)
(c_parser_alignas_specifier, c_parser_initelt, c_parser_label):
Call convert_lvalue_to_rvalue on expressions required to be
integer constant expressions.
(c_parser_omp_declare_reduction): Update call to start_init.
* c-tree.h (C_TYPE_FIELDS_NON_CONSTEXPR)
(C_DECL_DECLARED_CONSTEXPR): New macros.
(struct c_declspecs): Add constexpr_p.
(start_decl, convert_lvalue_to_rvalue, start_init): Update
prototypes.
* c-typeck.cc (require_constant_value, require_constant_elements):
Change to bool.
(require_constexpr_value, maybe_get_constexpr_init)
(constexpr_init_fits_real_type, check_constexpr_init): New.
(convert_lvalue_to_rvalue): Add new parameter for_init.  Call
maybe_get_constexpr_init.
(store_init_value): Update call to digest_init.
(digest_init): Add parameters int_const_expr, arith_const_expr and
require_constexpr.  Check constexpr initializers.
(constructor_top_level): Remove.
(struct initializer_stack): Remove top_level.  Add
require_constexpr_value.
(start_init): Remove parameter top_level.  Add parameters
init_require_constant and init_require_constexpr.  Save
require_constexpr_value on stack.
(pop_init_level): Use a null pointer constant for zero initializer
of pointer initialized with {}.
(output_init_element): Update call to digest_init.  Avoid passing
null pointer constants of pointer type through digest_init a
second time when initializing a constexpr object.

gcc/testsuite/
* gcc.dg/c11-keywords-1.c: Also test constexpr.
* gcc.dg/c2x-constexpr-1.c, gcc.dg/c2x-constexpr-2a.c,
gcc.dg/c2x-constexpr-2b.c, gcc.dg/c2x-constexpr-3.c,
gcc.dg/c2x-constexpr-4.c, gcc.dg/c2x-constexpr-5.c,
gcc.dg/c2x-constexpr-6.c, gcc.dg/c2x-constexpr-7.c,
gcc.dg/c2x-constexpr-8.c, gcc.dg/c2x-constexpr-9.c,
gcc.dg/dfp/c2x-constexpr-dfp-1.c,
gcc.dg/dfp/c2x-constexpr-dfp-2.c, gcc.dg/gnu2x-constexpr-1.c,
gcc.target/i386/excess-precision-11.c,
gcc.target/i386/excess-precision-12.c: New tests.
22 files changed:
gcc/c-family/c-common.cc
gcc/c/c-decl.cc
gcc/c/c-parser.cc
gcc/c/c-tree.h
gcc/c/c-typeck.cc
gcc/dfp.cc
gcc/testsuite/gcc.dg/c11-keywords-1.c
gcc/testsuite/gcc.dg/c2x-constexpr-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-constexpr-2a.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-constexpr-2b.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-constexpr-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-constexpr-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-constexpr-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-constexpr-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-constexpr-7.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-constexpr-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-constexpr-9.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu2x-constexpr-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/excess-precision-11.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/excess-precision-12.c [new file with mode: 0644]