[clang] aced81c - [C23] Implement N3018: The constexpr specifier for object definitions (#73099)

via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 6 00:46:39 PST 2024


Author: Mariya Podchishchaeva
Date: 2024-03-06T09:46:35+01:00
New Revision: aced81c0a5bf30dda99fde2e28364426de4c18d3

URL: https://github.com/llvm/llvm-project/commit/aced81c0a5bf30dda99fde2e28364426de4c18d3
DIFF: https://github.com/llvm/llvm-project/commit/aced81c0a5bf30dda99fde2e28364426de4c18d3.diff

LOG: [C23] Implement N3018: The constexpr specifier for object definitions (#73099)

The implementation mostly reuses C++ code paths where possible,
including narrowing check in order to provide diagnostic messages in
case initializer for constexpr variable is not exactly representable in
target type.

The following won't work due to lack of support for other features:
- Diagnosing of underspecified declarations involving constexpr
- Constexpr attached to compound literals

Also due to lack of support for char8_t some of examples with utf-8
strings don't work properly.

Fixes https://github.com/llvm/llvm-project/issues/64742

Added: 
    clang/test/C/C2x/n3018.c
    clang/test/Parser/c23-constexpr.c
    clang/test/Sema/constexpr.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/AST/Expr.h
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/TokenKinds.def
    clang/lib/AST/Decl.cpp
    clang/lib/AST/ExprConstant.cpp
    clang/lib/Parse/ParseDecl.cpp
    clang/lib/Sema/DeclSpec.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaInit.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/www/c_status.html

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b074055a4eaf69..0ff4a93b15ea8f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -143,6 +143,9 @@ C23 Feature Support
   macros typically exposed from ``<inttypes.h>``, such as ``PRIb8``.
   (`#81896: <https://github.com/llvm/llvm-project/issues/81896>`_).
 
+- Clang now supports `N3018 The constexpr specifier for object definitions`
+  <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3018.htm>`_.
+
 Non-comprehensive list of changes in this release
 -------------------------------------------------
 

diff  --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index bf0622bdeca30e..f7857d63bdef08 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -1875,6 +1875,17 @@ class StringLiteral final
     llvm_unreachable("Unsupported character width!");
   }
 
+  // Get code unit but preserve sign info.
+  int64_t getCodeUnitS(size_t I, uint64_t BitWidth) const {
+    int64_t V = getCodeUnit(I);
+    if (isOrdinary() || isWide()) {
+      unsigned Width = getCharByteWidth() * BitWidth;
+      llvm::APInt AInt(Width, (uint64_t)V);
+      V = AInt.getSExtValue();
+    }
+    return V;
+  }
+
   unsigned getByteLength() const { return getCharByteWidth() * getLength(); }
   unsigned getLength() const { return *getTrailingObjects<unsigned>(); }
   unsigned getCharByteWidth() const { return StringLiteralBits.CharByteWidth; }

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 708b3c7f1ede0a..b007ff7d8ccf0f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2946,6 +2946,18 @@ def warn_private_extern : Warning<
 def note_private_extern : Note<
   "use __attribute__((visibility(\"hidden\"))) attribute instead">;
 
+// C23 constexpr
+def err_c23_constexpr_not_variable : Error<
+  "'constexpr' can only be used in variable declarations">;
+def err_c23_constexpr_invalid_type : Error<
+  "constexpr variable cannot have type %0">;
+def err_c23_constexpr_init_not_representable : Error<
+  "constexpr initializer evaluates to %0 which is not exactly representable in type %1">;
+def err_c23_constexpr_init_type_mismatch : Error<
+  "constexpr initializer for type %0 is of type %1">;
+def err_c23_constexpr_pointer_not_null : Error<
+  "constexpr pointer initializer is not null">;
+
 // C++ Concepts
 def err_concept_decls_may_only_appear_in_global_namespace_scope : Error<
   "concept declarations may only appear in global or namespace scope">;

diff  --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 1d16e4843615d9..3a96f8a4d22bd1 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -393,7 +393,7 @@ CXX11_KEYWORD(alignas               , KEYC23)
 CXX11_UNARY_EXPR_OR_TYPE_TRAIT(alignof, AlignOf, KEYC23)
 CXX11_KEYWORD(char16_t              , KEYNOMS18)
 CXX11_KEYWORD(char32_t              , KEYNOMS18)
-CXX11_KEYWORD(constexpr             , 0)
+CXX11_KEYWORD(constexpr             , KEYC23)
 CXX11_KEYWORD(decltype              , 0)
 CXX11_KEYWORD(noexcept              , 0)
 CXX11_KEYWORD(nullptr               , KEYC23)

diff  --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 57a92357f6e5ba..59c039f1f8daeb 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2465,7 +2465,7 @@ bool VarDecl::mightBeUsableInConstantExpressions(const ASTContext &C) const {
 
   // OpenCL permits const integral variables to be used in constant
   // expressions, like in C++98.
-  if (!Lang.CPlusPlus && !Lang.OpenCL)
+  if (!Lang.CPlusPlus && !Lang.OpenCL && !Lang.C23)
     return false;
 
   // Function parameters are never usable in constant expressions.
@@ -2487,14 +2487,19 @@ bool VarDecl::mightBeUsableInConstantExpressions(const ASTContext &C) const {
   if (!getType().isConstant(C) || getType().isVolatileQualified())
     return false;
 
-  // In C++, const, non-volatile variables of integral or enumeration types
-  // can be used in constant expressions.
-  if (getType()->isIntegralOrEnumerationType())
+  // In C++, but not in C, const, non-volatile variables of integral or
+  // enumeration types can be used in constant expressions.
+  if (getType()->isIntegralOrEnumerationType() && !Lang.C23)
     return true;
 
+  // C23 6.6p7: An identifier that is:
+  // ...
+  // - declared with storage-class specifier constexpr and has an object type,
+  // is a named constant, ... such a named constant is a constant expression
+  // with the type and value of the declared object.
   // Additionally, in C++11, non-volatile constexpr variables can be used in
   // constant expressions.
-  return Lang.CPlusPlus11 && isConstexpr();
+  return (Lang.CPlusPlus11 || Lang.C23) && isConstexpr();
 }
 
 bool VarDecl::isUsableInConstantExpressions(const ASTContext &Context) const {
@@ -2572,11 +2577,11 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
   bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes,
                                             IsConstantInitialization);
 
-  // In C++, this isn't a constant initializer if we produced notes. In that
+  // In C++/C23, this isn't a constant initializer if we produced notes. In that
   // case, we can't keep the result, because it may only be correct under the
   // assumption that the initializer is a constant context.
-  if (IsConstantInitialization && Ctx.getLangOpts().CPlusPlus &&
-      !Notes.empty())
+  if (IsConstantInitialization &&
+      (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23) && !Notes.empty())
     Result = false;
 
   // Ensure the computed APValue is cleaned up later if evaluation succeeded,
@@ -2634,7 +2639,9 @@ bool VarDecl::checkForConstantInitialization(
   // std::is_constant_evaluated()).
   assert(!Eval->WasEvaluated &&
          "already evaluated var value before checking for constant init");
-  assert(getASTContext().getLangOpts().CPlusPlus && "only meaningful in C++");
+  assert((getASTContext().getLangOpts().CPlusPlus ||
+          getASTContext().getLangOpts().C23) &&
+         "only meaningful in C++/C23");
 
   assert(!getInit()->isValueDependent());
 

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4b6ccafbb7f0ed..d8ca35740fbc35 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -4133,6 +4133,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
     }
 
     bool IsConstant = BaseType.isConstant(Info.Ctx);
+    bool ConstexprVar = false;
+    if (const auto *VD = dyn_cast_if_present<VarDecl>(
+            Info.EvaluatingDecl.dyn_cast<const ValueDecl *>()))
+      ConstexprVar = VD->isConstexpr();
 
     // Unless we're looking at a local variable or argument in a constexpr call,
     // the variable we're reading must be const.
@@ -4152,6 +4156,9 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
         return CompleteObject();
       } else if (VD->isConstexpr()) {
         // OK, we can read this variable.
+      } else if (Info.getLangOpts().C23 && ConstexprVar) {
+        Info.FFDiag(E);
+        return CompleteObject();
       } else if (BaseType->isIntegralOrEnumerationType()) {
         if (!IsConstant) {
           if (!IsAccess)
@@ -15826,7 +15833,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
   EStatus.Diag = &Notes;
 
   EvalInfo Info(Ctx, EStatus,
-                (IsConstantInitialization && Ctx.getLangOpts().CPlusPlus)
+                (IsConstantInitialization &&
+                 (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
                     ? EvalInfo::EM_ConstantExpression
                     : EvalInfo::EM_ConstantFold);
   Info.setEvaluatingDecl(VD, Value);

diff  --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index ccbfea6a66fba7..81f1c711269445 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4265,6 +4265,8 @@ void Parser::ParseDeclarationSpecifiers(
 
     // constexpr, consteval, constinit specifiers
     case tok::kw_constexpr:
+      if (getLangOpts().C23)
+        Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName();
       isInvalid = DS.SetConstexprSpec(ConstexprSpecKind::Constexpr, Loc,
                                       PrevSpec, DiagID);
       break;

diff  --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index aede602f1de84c..b79683bb32a69e 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1377,6 +1377,20 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) {
       ThreadStorageClassSpec = TSCS_unspecified;
       ThreadStorageClassSpecLoc = SourceLocation();
     }
+    if (S.getLangOpts().C23 &&
+        getConstexprSpecifier() == ConstexprSpecKind::Constexpr) {
+      S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination)
+          << DeclSpec::getSpecifierName(getThreadStorageClassSpec())
+          << SourceRange(getThreadStorageClassSpecLoc());
+    }
+  }
+
+  if (S.getLangOpts().C23 &&
+      getConstexprSpecifier() == ConstexprSpecKind::Constexpr &&
+      StorageClassSpec == SCS_extern) {
+    S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination)
+        << DeclSpec::getSpecifierName(getStorageClassSpec())
+        << SourceRange(getStorageClassSpecLoc());
   }
 
   // If no type specifier was provided and we're parsing a language where

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8fcaf6ab312b04..210e2835184a95 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5147,6 +5147,8 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
       Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_tag)
           << GetDiagnosticTypeSpecifierID(DS)
           << static_cast<int>(DS.getConstexprSpecifier());
+    else if (getLangOpts().C23)
+      Diag(DS.getConstexprSpecLoc(), diag::err_c23_constexpr_not_variable);
     else
       Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_wrong_decl_kind)
           << static_cast<int>(DS.getConstexprSpecifier());
@@ -8649,6 +8651,38 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND,
   return false;
 }
 
+static bool CheckC23ConstexprVarType(Sema &SemaRef, SourceLocation VarLoc,
+                                     QualType T) {
+  QualType CanonT = SemaRef.Context.getCanonicalType(T);
+  // C23 6.7.1p5: An object declared with storage-class specifier constexpr or
+  // any of its members, even recursively, shall not have an atomic type, or a
+  // variably modified type, or a type that is volatile or restrict qualified.
+  if (CanonT->isVariablyModifiedType()) {
+    SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
+    return true;
+  }
+
+  // Arrays are qualified by their element type, so get the base type (this
+  // works on non-arrays as well).
+  CanonT = SemaRef.Context.getBaseElementType(CanonT);
+
+  if (CanonT->isAtomicType() || CanonT.isVolatileQualified() ||
+      CanonT.isRestrictQualified()) {
+    SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
+    return true;
+  }
+
+  if (CanonT->isRecordType()) {
+    const RecordDecl *RD = CanonT->getAsRecordDecl();
+    if (llvm::any_of(RD->fields(), [&SemaRef, VarLoc](const FieldDecl *F) {
+          return CheckC23ConstexprVarType(SemaRef, VarLoc, F->getType());
+        }))
+      return true;
+  }
+
+  return false;
+}
+
 void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
   // If the decl is already known invalid, don't check it.
   if (NewVD->isInvalidDecl())
@@ -8899,6 +8933,12 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
     return;
   }
 
+  if (getLangOpts().C23 && NewVD->isConstexpr() &&
+      CheckC23ConstexprVarType(*this, NewVD->getLocation(), T)) {
+    NewVD->setInvalidDecl();
+    return;
+  }
+
   if (NewVD->isConstexpr() && !T->isDependentType() &&
       RequireLiteralType(NewVD->getLocation(), T,
                          diag::err_constexpr_var_non_literal)) {
@@ -9281,6 +9321,22 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
   FunctionDecl *NewFD = nullptr;
   bool isInline = D.getDeclSpec().isInlineSpecified();
 
+  ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier();
+  if (ConstexprKind == ConstexprSpecKind::Constinit ||
+      (SemaRef.getLangOpts().C23 &&
+       ConstexprKind == ConstexprSpecKind::Constexpr)) {
+
+    if (SemaRef.getLangOpts().C23)
+      SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
+                   diag::err_c23_constexpr_not_variable);
+    else
+      SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
+                   diag::err_constexpr_wrong_decl_kind)
+          << static_cast<int>(ConstexprKind);
+    ConstexprKind = ConstexprSpecKind::Unspecified;
+    D.getMutableDeclSpec().ClearConstexprSpec();
+  }
+
   if (!SemaRef.getLangOpts().CPlusPlus) {
     // Determine whether the function was written with a prototype. This is
     // true when:
@@ -9314,15 +9370,6 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
   }
 
   ExplicitSpecifier ExplicitSpecifier = D.getDeclSpec().getExplicitSpecifier();
-
-  ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier();
-  if (ConstexprKind == ConstexprSpecKind::Constinit) {
-    SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
-                 diag::err_constexpr_wrong_decl_kind)
-        << static_cast<int>(ConstexprKind);
-    ConstexprKind = ConstexprSpecKind::Unspecified;
-    D.getMutableDeclSpec().ClearConstexprSpec();
-  }
   Expr *TrailingRequiresClause = D.getTrailingRequiresClause();
 
   SemaRef.CheckExplicitObjectMemberFunction(DC, D, Name, R);
@@ -13909,7 +13956,9 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
       VDecl->setStorageClass(SC_Extern);
 
     // C99 6.7.8p4. All file scoped initializers need to be constant.
-    if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl())
+    // Avoid duplicate diagnostics for constexpr variables.
+    if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl() &&
+        !VDecl->isConstexpr())
       CheckForConstantInitializer(Init, DclT);
   }
 
@@ -14520,9 +14569,13 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
   QualType baseType = Context.getBaseElementType(type);
   bool HasConstInit = true;
 
+  if (getLangOpts().C23 && var->isConstexpr() && !Init)
+    Diag(var->getLocation(), diag::err_constexpr_var_requires_const_init)
+        << var;
+
   // Check whether the initializer is sufficiently constant.
-  if (getLangOpts().CPlusPlus && !type->isDependentType() && Init &&
-      !Init->isValueDependent() &&
+  if ((getLangOpts().CPlusPlus || (getLangOpts().C23 && var->isConstexpr())) &&
+      !type->isDependentType() && Init && !Init->isValueDependent() &&
       (GlobalStorage || var->isConstexpr() ||
        var->mightBeUsableInConstantExpressions(Context))) {
     // If this variable might have a constant initializer or might be usable in
@@ -14530,7 +14583,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
     // do this lazily, because the result might depend on things that change
     // later, such as which constexpr functions happen to be defined.
     SmallVector<PartialDiagnosticAt, 8> Notes;
-    if (!getLangOpts().CPlusPlus11) {
+    if (!getLangOpts().CPlusPlus11 && !getLangOpts().C23) {
       // Prior to C++11, in contexts where a constant initializer is required,
       // the set of valid constant initializers is described by syntactic rules
       // in [expr.const]p2-6.

diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 060fe35ad96808..011deed7a9a9f1 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -190,13 +190,35 @@ static void updateGNUCompoundLiteralRValue(Expr *E) {
   }
 }
 
+static bool initializingConstexprVariable(const InitializedEntity &Entity) {
+  Decl *D = Entity.getDecl();
+  const InitializedEntity *Parent = &Entity;
+
+  while (Parent) {
+    D = Parent->getDecl();
+    Parent = Parent->getParent();
+  }
+
+  if (const auto *VD = dyn_cast_if_present<VarDecl>(D); VD && VD->isConstexpr())
+    return true;
+
+  return false;
+}
+
+static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
+                                               Sema &SemaRef, QualType &TT);
+
 static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT,
-                            Sema &S) {
+                            Sema &S, bool CheckC23ConstexprInit = false) {
   // Get the length of the string as parsed.
   auto *ConstantArrayTy =
       cast<ConstantArrayType>(Str->getType()->getAsArrayTypeUnsafe());
   uint64_t StrLength = ConstantArrayTy->getSize().getZExtValue();
 
+  if (CheckC23ConstexprInit)
+    if (const StringLiteral *SL = dyn_cast<StringLiteral>(Str->IgnoreParens()))
+      CheckC23ConstexprInitStringLiteral(SL, S, DeclT);
+
   if (const IncompleteArrayType *IAT = dyn_cast<IncompleteArrayType>(AT)) {
     // C99 6.7.8p14. We have an array of character type with unknown size
     // being initialized to a string literal.
@@ -1476,7 +1498,9 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
     if (IsStringInit(expr, arrayType, SemaRef.Context) == SIF_None) {
       // FIXME: Should we do this checking in verify-only mode?
       if (!VerifyOnly)
-        CheckStringInit(expr, ElemType, arrayType, SemaRef);
+        CheckStringInit(expr, ElemType, arrayType, SemaRef,
+                        SemaRef.getLangOpts().C23 &&
+                            initializingConstexprVariable(Entity));
       if (StructuredList)
         UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
       ++Index;
@@ -1941,7 +1965,9 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
       // constant for each string.
       // FIXME: Should we do these checks in verify-only mode too?
       if (!VerifyOnly)
-        CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef);
+        CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef,
+                        SemaRef.getLangOpts().C23 &&
+                            initializingConstexprVariable(Entity));
       if (StructuredList) {
         UpdateStructuredListElement(StructuredList, StructuredIndex,
                                     IList->getInit(Index));
@@ -8377,6 +8403,9 @@ static void DiagnoseNarrowingInInitList(Sema &S,
                                         QualType EntityType,
                                         const Expr *PostInit);
 
+static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
+                                            QualType ToType, Expr *Init);
+
 /// Provide warnings when std::move is used on construction.
 static void CheckMoveOnConstruction(Sema &S, const Expr *InitExpr,
                                     bool IsReturnStmt) {
@@ -9203,6 +9232,23 @@ ExprResult InitializationSequence::Perform(Sema &S,
         return ExprError();
       CurInit = CurInitExprRes;
 
+      if (S.getLangOpts().C23 && initializingConstexprVariable(Entity)) {
+        CheckC23ConstexprInitConversion(S, SourceType, Entity.getType(),
+                                        CurInit.get());
+
+        // C23 6.7.1p6: If an object or subobject declared with storage-class
+        // specifier constexpr has pointer, integer, or arithmetic type, any
+        // explicit initializer value for it shall be null, an integer
+        // constant expression, or an arithmetic constant expression,
+        // respectively.
+        Expr::EvalResult ER;
+        if (Entity.getType()->getAs<PointerType>() &&
+            CurInit.get()->EvaluateAsRValue(ER, S.Context) &&
+            !ER.Val.isNullPointer()) {
+          S.Diag(Kind.getLocation(), diag::err_c23_constexpr_pointer_not_null);
+        }
+      }
+
       bool Complained;
       if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(),
                                      Step->Type, SourceType,
@@ -9220,7 +9266,9 @@ ExprResult InitializationSequence::Perform(Sema &S,
       QualType Ty = Step->Type;
       bool UpdateType = ResultType && Entity.getType()->isIncompleteArrayType();
       CheckStringInit(CurInit.get(), UpdateType ? *ResultType : Ty,
-                      S.Context.getAsArrayType(Ty), S);
+                      S.Context.getAsArrayType(Ty), S,
+                      S.getLangOpts().C23 &&
+                          initializingConstexprVariable(Entity));
       break;
     }
 
@@ -10509,6 +10557,69 @@ static void DiagnoseNarrowingInInitList(Sema &S,
              S.getLocForEndOfToken(PostInit->getEndLoc()), ")");
 }
 
+static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
+                                            QualType ToType, Expr *Init) {
+  assert(S.getLangOpts().C23);
+  ImplicitConversionSequence ICS = S.TryImplicitConversion(
+      Init->IgnoreParenImpCasts(), ToType, /*SuppressUserConversions*/ false,
+      Sema::AllowedExplicit::None,
+      /*InOverloadResolution*/ false,
+      /*CStyle*/ false,
+      /*AllowObjCWritebackConversion=*/false);
+
+  if (!ICS.isStandard())
+    return;
+
+  APValue Value;
+  QualType PreNarrowingType;
+  // Reuse C++ narrowing check.
+  switch (ICS.Standard.getNarrowingKind(
+      S.Context, Init, Value, PreNarrowingType,
+      /*IgnoreFloatToIntegralConversion*/ false)) {
+  // The value doesn't fit.
+  case NK_Constant_Narrowing:
+    S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_not_representable)
+        << Value.getAsString(S.Context, PreNarrowingType) << ToType;
+    return;
+
+  // Conversion to a narrower type.
+  case NK_Type_Narrowing:
+    S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_type_mismatch)
+        << ToType << FromType;
+    return;
+
+  // Since we only reuse narrowing check for C23 constexpr variables here, we're
+  // not really interested in these cases.
+  case NK_Dependent_Narrowing:
+  case NK_Variable_Narrowing:
+  case NK_Not_Narrowing:
+    return;
+  }
+  llvm_unreachable("unhandled case in switch");
+}
+
+static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
+                                               Sema &SemaRef, QualType &TT) {
+  assert(SemaRef.getLangOpts().C23);
+  // character that string literal contains fits into TT - target type.
+  const ArrayType *AT = SemaRef.Context.getAsArrayType(TT);
+  QualType CharType = AT->getElementType();
+  uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType);
+  bool isUnsigned = CharType->isUnsignedIntegerType();
+  llvm::APSInt Value(BitWidth, isUnsigned);
+  for (unsigned I = 0, N = SE->getLength(); I != N; ++I) {
+    int64_t C = SE->getCodeUnitS(I, SemaRef.Context.getCharWidth());
+    Value = C;
+    if (Value != C) {
+      SemaRef.Diag(SemaRef.getLocationOfStringLiteralByte(SE, I),
+                   diag::err_c23_constexpr_init_not_representable)
+          << C << CharType;
+      return;
+    }
+  }
+  return;
+}
+
 //===----------------------------------------------------------------------===//
 // Initialization helper functions
 //===----------------------------------------------------------------------===//

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 7d38043890ca20..a03f3eae5478eb 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -332,7 +332,8 @@ static const Expr *IgnoreNarrowingConversion(ASTContext &Ctx,
 NarrowingKind StandardConversionSequence::getNarrowingKind(
     ASTContext &Ctx, const Expr *Converted, APValue &ConstantValue,
     QualType &ConstantType, bool IgnoreFloatToIntegralConversion) const {
-  assert(Ctx.getLangOpts().CPlusPlus && "narrowing check outside C++");
+  assert((Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23) &&
+         "narrowing check outside C++");
 
   // C++11 [dcl.init.list]p7:
   //   A narrowing conversion is an implicit conversion ...
@@ -414,20 +415,41 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
       if (Initializer->isValueDependent())
         return NK_Dependent_Narrowing;
 
-      if (Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) {
+      Expr::EvalResult R;
+      if ((Ctx.getLangOpts().C23 && Initializer->EvaluateAsRValue(R, Ctx)) ||
+          Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) {
         // Constant!
+        if (Ctx.getLangOpts().C23)
+          ConstantValue = R.Val;
         assert(ConstantValue.isFloat());
         llvm::APFloat FloatVal = ConstantValue.getFloat();
         // Convert the source value into the target type.
         bool ignored;
-        llvm::APFloat::opStatus ConvertStatus = FloatVal.convert(
-          Ctx.getFloatTypeSemantics(ToType),
-          llvm::APFloat::rmNearestTiesToEven, &ignored);
-        // If there was no overflow, the source value is within the range of
-        // values that can be represented.
-        if (ConvertStatus & llvm::APFloat::opOverflow) {
-          ConstantType = Initializer->getType();
-          return NK_Constant_Narrowing;
+        llvm::APFloat Converted = FloatVal;
+        llvm::APFloat::opStatus ConvertStatus =
+            Converted.convert(Ctx.getFloatTypeSemantics(ToType),
+                              llvm::APFloat::rmNearestTiesToEven, &ignored);
+        Converted.convert(Ctx.getFloatTypeSemantics(FromType),
+                          llvm::APFloat::rmNearestTiesToEven, &ignored);
+        if (Ctx.getLangOpts().C23) {
+          if (FloatVal.isNaN() && Converted.isNaN() &&
+              !FloatVal.isSignaling() && !Converted.isSignaling()) {
+            // Quiet NaNs are considered the same value, regardless of
+            // payloads.
+            return NK_Not_Narrowing;
+          }
+          // For normal values, check exact equality.
+          if (!Converted.bitwiseIsEqual(FloatVal)) {
+            ConstantType = Initializer->getType();
+            return NK_Constant_Narrowing;
+          }
+        } else {
+          // If there was no overflow, the source value is within the range of
+          // values that can be represented.
+          if (ConvertStatus & llvm::APFloat::opOverflow) {
+            ConstantType = Initializer->getType();
+            return NK_Constant_Narrowing;
+          }
         }
       } else {
         return NK_Variable_Narrowing;
@@ -494,7 +516,30 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
     }
     return NK_Not_Narrowing;
   }
+  case ICK_Complex_Real:
+    if (FromType->isComplexType() && !ToType->isComplexType())
+      return NK_Type_Narrowing;
+    return NK_Not_Narrowing;
 
+  case ICK_Floating_Promotion:
+    if (Ctx.getLangOpts().C23) {
+      const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
+      Expr::EvalResult R;
+      if (Initializer->EvaluateAsRValue(R, Ctx)) {
+        ConstantValue = R.Val;
+        assert(ConstantValue.isFloat());
+        llvm::APFloat FloatVal = ConstantValue.getFloat();
+        // C23 6.7.3p6 If the initializer has real type and a signaling NaN
+        // value, the unqualified versions of the type of the initializer and
+        // the corresponding real type of the object declared shall be
+        // compatible.
+        if (FloatVal.isNaN() && FloatVal.isSignaling()) {
+          ConstantType = Initializer->getType();
+          return NK_Constant_Narrowing;
+        }
+      }
+    }
+    return NK_Not_Narrowing;
   default:
     // Other kinds of conversions are not narrowings.
     return NK_Not_Narrowing;

diff  --git a/clang/test/C/C2x/n3018.c b/clang/test/C/C2x/n3018.c
new file mode 100644
index 00000000000000..0d54d53b7499fc
--- /dev/null
+++ b/clang/test/C/C2x/n3018.c
@@ -0,0 +1,87 @@
+// RUN: %clang_cc1 -std=c23 -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion %s
+
+/* WG14 N3018: Full
+ * The constexpr specifier for object definitions
+ */
+
+#define ULLONG_MAX (__LONG_LONG_MAX__*2ULL+1ULL)
+#define UINT_MAX  (__INT_MAX__  *2U +1U)
+
+void Example0() {
+  constexpr unsigned int minusOne    = -1;
+  // expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned int'}}
+  constexpr unsigned int uint_max    = -1U;
+  constexpr double onethird          = 1.0/3.0;
+  constexpr double onethirdtrunc     = (double)(1.0/3.0);
+
+  constexpr char string[] = { "\xFF", };
+  constexpr unsigned char ucstring[] = { "\xFF", };
+  // expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+  constexpr char string1[] = { -1, 0, };
+  constexpr unsigned char ucstring1[] = { -1, 0, };
+  // expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+
+  // TODO: Make sure these work correctly once char8_t and _Decimal are supported
+  // constexpr char8_t u8string[] = { 255, 0, }; // ok
+  // constexpr char8_t u8string[]       = { u8"\xFF", };     // ok
+  // constexpr _Decimal32 small         = DEC64_TRUE_MIN * 0;// constraint violation
+}
+
+void Example1() {
+  constexpr int K = 47;
+  enum {
+      A = K,
+  };
+  constexpr int L = K;
+  static int b    = K + 1;
+  int array[K];
+  _Static_assert(K == 47);
+}
+
+constexpr int K = 47;
+static const int b = K + 1;
+
+void Example2() {
+  constexpr int A          = 42LL;
+  constexpr signed short B = ULLONG_MAX;
+  // expected-error at -1 {{constexpr initializer evaluates to 18446744073709551615 which is not exactly representable in type 'const short'}}
+  constexpr float C        = 47u;
+
+  constexpr float D = 432000000;
+  constexpr float E = 1.0 / 3.0;
+  // expected-error at -1 {{constexpr initializer evaluates to 3.333333e-01 which is not exactly representable in type 'const float'}}
+  constexpr float F = 1.0f / 3.0f;
+}
+
+
+void Example3() {
+  constexpr static unsigned short array[] = {
+      3000,
+      300000,
+      // expected-error at -1 {{constexpr initializer evaluates to 300000 which is not exactly representable in type 'const unsigned short'}}
+      -1
+      // expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned short'}}
+  };
+
+  constexpr static unsigned short array1[] = {
+      3000,
+      3000,
+      -1
+       // expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned short'}}
+  };
+
+  struct S {
+      int x, y;
+  };
+  constexpr struct S s = {
+      .x = __INT_MAX__,
+      .y = UINT_MAX,
+      // expected-error at -1 {{constexpr initializer evaluates to 4294967295 which is not exactly representable in type 'int'}}
+  };
+}
+
+void Example4() {
+  struct s { void *p; };
+  constexpr struct s A = { nullptr };
+  constexpr struct s B = A;
+}

diff  --git a/clang/test/Parser/c23-constexpr.c b/clang/test/Parser/c23-constexpr.c
new file mode 100644
index 00000000000000..156128fa0745cd
--- /dev/null
+++ b/clang/test/Parser/c23-constexpr.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=c23 -std=c23 %s -Wpre-c2x-compat
+// RUN: %clang_cc1 -fsyntax-only -verify=c17 -std=c17 %s
+
+constexpr int a = 0; // c17-error {{unknown type name 'constexpr'}} \
+                        c23-warning {{'constexpr' is incompatible with C standards before C23}}
+
+void func(int array[constexpr]); // c23-error {{expected expression}} \
+                                 // c17-error {{use of undeclared}}
+
+_Atomic constexpr int b = 0; // c23-error {{constexpr variable cannot have type 'const _Atomic(int)'}} \
+                             // c23-warning {{'constexpr' is incompatible with C standards before C23}} \
+                             // c17-error {{unknown type name 'constexpr'}}
+
+int static constexpr c = 1; // c17-error {{expected ';' after top level declarator}} \
+                            // c23-warning {{'constexpr' is incompatible with C standards before C23}}

diff  --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
new file mode 100644
index 00000000000000..8286cd2107d2f2
--- /dev/null
+++ b/clang/test/Sema/constexpr.c
@@ -0,0 +1,359 @@
+// RUN: %clang_cc1 -std=c23 -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion -Wno-div-by-zero %s
+
+// Check that constexpr only applies to variables.
+constexpr void f0() {} // expected-error {{'constexpr' can only be used in variable declarations}}
+constexpr const int f1() { return 0; } // expected-error {{'constexpr' can only be used in variable declarations}}
+
+constexpr struct S1 { int f; }; //expected-error {{struct cannot be marked constexpr}}
+constexpr struct S2 ; // expected-error {{struct cannot be marked constexpr}}
+constexpr union U1; // expected-error {{union cannot be marked constexpr}}
+constexpr union U2 {int a; float b;}; // expected-error {{union cannot be marked constexpr}}
+constexpr enum E1 {A = 1, B = 2} ; // expected-error {{enum cannot be marked constexpr}}
+struct S3 {
+  static constexpr int f = 0; // expected-error {{type name does not allow storage class}}
+  // expected-error at -1 {{type name does not allow constexpr}}
+  // expected-error at -2 {{expected ';' at end}}
+  constexpr int f1 = 0;
+  // expected-error at -1 {{type name does not allow constexpr}}
+  // expected-error at -2 {{expected ';' at end}}
+};
+
+constexpr; // expected-error {{'constexpr' can only be used in variable declarations}}
+constexpr int V1 = 3;
+constexpr float V2 = 7.0;
+int V3 = (constexpr)3; // expected-error {{expected expression}}
+
+void f2() {
+  constexpr int a = 0;
+  constexpr float b = 1.7f;
+}
+
+// Check how constexpr works with other storage-class specifiers.
+constexpr auto V4 = 1;
+constexpr static auto V5 = 1;
+constexpr static const auto V6 = 1;
+constexpr static const int V7 = 1;
+constexpr static int V8 = 1;
+constexpr auto Ulong = 1L;
+constexpr auto CompoundLiteral = (int){13};
+constexpr auto DoubleCast = (double)(1 / 3);
+constexpr auto String = "this is a string"; // expected-error {{constexpr pointer initializer is not null}}
+constexpr signed auto Long = 1L; // expected-error {{'auto' cannot be signed or unsigned}}
+_Static_assert(_Generic(Ulong, long : 1));
+_Static_assert(_Generic(CompoundLiteral, int : 1));
+_Static_assert(_Generic(DoubleCast, double : 1));
+_Static_assert(_Generic(String, char* : 1));
+
+typedef constexpr int Foo; // expected-error {{typedef cannot be constexpr}}
+constexpr typedef int Bar; // expected-error {{typedef cannot be constexpr}}
+
+void f3(constexpr register int P1) { // expected-error {{function parameter cannot be constexpr}}
+  constexpr register int V9 = 0;
+  constexpr register auto V10 = 0.0;
+}
+
+constexpr thread_local int V11 = 38; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
+constexpr static thread_local double V12 = 38; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
+constexpr extern thread_local char V13; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
+// expected-error at -1 {{cannot combine with previous 'extern' declaration specifier}}
+// expected-error at -2 {{constexpr variable declaration must be a definition}}
+constexpr thread_local short V14 = 38; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
+
+// Check how constexpr works with qualifiers.
+constexpr _Atomic int V15 = 0; // expected-error {{constexpr variable cannot have type 'const _Atomic(int)'}}
+constexpr _Atomic(int) V16 = 0; // expected-error {{constexpr variable cannot have type 'const _Atomic(int)'}}
+
+constexpr volatile int V17 = 0; // expected-error {{constexpr variable cannot have type 'const volatile int'}}
+
+constexpr int * restrict V18 = 0; // expected-error {{constexpr variable cannot have type 'int *const restrict'}}
+
+constexpr extern char Oops = 1; // expected-error {{cannot combine with previous 'extern' declaration specifier}} \
+                                // expected-warning {{'extern' variable has an initializer}}
+
+constexpr int * restrict * Oops1 = 0;
+
+typedef _Atomic(int) TheA;
+typedef volatile short TheV;
+typedef float * restrict TheR;
+
+constexpr TheA V19[3] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'const TheA[3]' (aka 'const _Atomic(int)[3]')}}
+constexpr TheV V20[3] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'const TheV[3]' (aka 'const volatile short[3]')}}
+constexpr TheR V21[3] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'const TheR[3]' (aka 'float *restrict const[3]')}}
+
+struct HasA {
+  TheA f;
+  int b;
+};
+
+struct HasV {
+  float b;
+  TheV f;
+};
+
+struct HasR {
+  short b;
+  int a;
+  TheR f;
+};
+
+constexpr struct HasA V22[2] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
+constexpr struct HasV V23[2] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'TheV' (aka 'volatile short')}}
+constexpr struct HasR V24[2] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'TheR' (aka 'float *restrict')}}
+
+union U3 {
+  float a;
+  union {
+    struct HasA f;
+    struct HasR f1;
+  };
+};
+
+constexpr union U3 V25 = {};
+// expected-error at -1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
+constexpr union U3 V26[8] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
+
+struct S4 {
+  union U3 f[3];
+};
+
+constexpr struct S4 V27 = {};
+// expected-error at -1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
+constexpr const int V28 = 28;
+
+struct S {
+  union {
+    volatile int i;
+  };
+  int j;
+};
+
+constexpr struct S s = {}; // expected-error {{constexpr variable cannot have type 'volatile int'}}
+
+// Check that constexpr variable must have a valid initializer which is a
+// constant expression.
+constexpr int V29;
+// expected-error at -1 {{constexpr variable 'V29' must be initialized by a constant expression}}
+
+struct S5 {
+  int f;
+};
+
+constexpr struct S5 V30;
+// expected-error at -1 {{constexpr variable 'V30' must be initialized by a constant expression}}
+constexpr struct S5 V31 = {};
+
+int randomFoo() { return 7; }
+
+constexpr float V32 = randomFoo();
+// expected-error at -1 {{constexpr variable 'V32' must be initialized by a constant expression}}
+
+const int V33 = 4;
+const int V34 = 0;
+const int V35 = 2;
+
+constexpr int V36 = V33 / V34;
+// expected-error at -1 {{constexpr variable 'V36' must be initialized by a constant expression}}
+constexpr int V37 = V33 / V35;
+// expected-error at -1 {{constexpr variable 'V37' must be initialized by a constant expression}}
+constexpr int V38 = 3;
+constexpr int V39 = V38 / V38;
+constexpr int V40 = V38 / 2;
+constexpr int V41 = V38 / 0;
+// expected-error at -1 {{constexpr variable 'V41' must be initialized by a constant expression}}
+// expected-note at -2 {{division by zero}}
+constexpr int V42 = V38 & 0;
+
+constexpr struct S5 V43 = { randomFoo() };
+// expected-error at -1 {{constexpr variable 'V43' must be initialized by a constant expression}}
+constexpr struct S5 V44 = { 0 };
+constexpr struct S5 V45 = { V38 / 0 };
+// expected-error at -1 {{constexpr variable 'V45' must be initialized by a constant expression}}
+// expected-note at -2 {{division by zero}}
+
+constexpr float V46[3] = {randomFoo() };
+// expected-error at -1 {{constexpr variable 'V46' must be initialized by a constant expression}}
+constexpr struct S5 V47[3] = {randomFoo() };
+// expected-error at -1 {{constexpr variable 'V47' must be initialized by a constant expression}}
+
+const static int V48 = V38;
+constexpr static int V49 = V48;
+// expected-error at -1 {{constexpr variable 'V49' must be initialized by a constant expression}}
+
+void f4(const int P1) {
+  constexpr int V = P1;
+// expected-error at -1 {{constexpr variable 'V' must be initialized by a constant expression}}
+
+  constexpr int V1 = 12;
+  constexpr const int *V2 = &V1;
+// expected-error at -1 {{constexpr variable 'V2' must be initialized by a constant expression}}
+}
+
+// Check that initializer for constexpr variable should match the type of the
+// variable and is exactly representable int the variable's type.
+
+struct S6 {
+  unsigned char a;
+};
+
+struct S7 {
+  union {
+    float a;
+  };
+  unsigned int b;
+};
+
+struct S8 {
+  unsigned char a[3];
+  unsigned int b[3];
+};
+
+constexpr struct S8 DesigInit = {.b = {299, 7, 8}, .a = {-1, 7, 8}};
+// expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned char'}}
+
+void f5() {
+  constexpr char V50 = 300;
+  // expected-error at -1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'const char'}}
+  constexpr float V51 = 1.0 / 3.0;
+  // expected-error at -1 {{constexpr initializer evaluates to 3.333333e-01 which is not exactly representable in type 'const float'}}
+  constexpr float V52 = 0.7;
+  // expected-error at -1 {{constexpr initializer evaluates to 7.000000e-01 which is not exactly representable in type 'const float'}}
+  constexpr float V53 = 1.0f / 3.0f;
+  constexpr float V54 = 432000000000;
+  // expected-error at -1 {{constexpr initializer evaluates to 432000000000 which is not exactly representable in type 'const float'}}
+  constexpr unsigned char V55[] = {
+      "\xAF",
+  // expected-error at -1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
+  };
+
+  constexpr unsigned char V56[] = {
+      u8"\xAF",
+  };
+  constexpr struct S6 V57 = {299};
+  // expected-error at -1 {{constexpr initializer evaluates to 299 which is not exactly representable in type 'unsigned char'}}
+  constexpr struct S6 V58 = {-299};
+  // expected-error at -1 {{constexpr initializer evaluates to -299 which is not exactly representable in type 'unsigned char'}}
+  constexpr double V59 = 0.5;
+  constexpr double V60 = 1.0;
+  constexpr float V61 = V59 / V60;
+  constexpr double V62 = 1.7;
+  constexpr float V63 = V59 / V62;
+  // expected-error at -1 {{constexpr initializer evaluates to 2.941176e-01 which is not exactly representable in type 'const float'}}
+
+  constexpr unsigned char V64 = '\xAF';
+  // expected-error at -1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
+  constexpr unsigned char V65 = u8'\xAF';
+
+  constexpr char V66[3] = {300};
+  // expected-error at -1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'const char'}}
+  constexpr struct S6 V67[3] = {300};
+  // expected-error at -1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'unsigned char'}}
+
+  constexpr struct S7 V68 = {0.3, -1 };
+  // expected-error at -1 {{constexpr initializer evaluates to 3.000000e-01 which is not exactly representable in type 'float'}}
+  // expected-error at -2 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned int'}}
+  constexpr struct S7 V69 = {0.5, -1 };
+  // expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned int'}}
+  constexpr struct S7 V70[3] = {{123456789}};
+  // expected-error at -1 {{constexpr initializer evaluates to 123456789 which is not exactly representable in type 'float'}}
+
+  constexpr int V71 = 0.3;
+  // expected-error at -1 {{constexpr initializer for type 'const int' is of type 'double'}}
+  constexpr int V72 = V59;
+  // expected-error at -1 {{constexpr initializer for type 'const int' is of type 'const double'}}
+  constexpr struct S6 V73 = {V59};
+  // expected-error at -1 {{constexpr initializer for type 'unsigned char' is of type 'const double'}}
+
+  constexpr float V74 = 1;
+  constexpr float V75 = V59;
+  constexpr unsigned int V76[3] = {0.5};
+  // expected-error at -1 {{constexpr initializer for type 'const unsigned int' is of type 'double'}}
+
+  constexpr _Complex float V77 = 0;
+  constexpr float V78 = V77;
+  // expected-error at -1 {{constexpr initializer for type 'const float' is of type 'const _Complex float'}}
+  constexpr int V79 = V77;
+  // expected-error at -1 {{constexpr initializer for type 'const int' is of type 'const _Complex float'}}
+
+}
+
+constexpr char string[] = "test""ing this out\xFF";
+constexpr unsigned char ustring[] = "test""ing this out\xFF";
+// expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+constexpr char u8string[] = u8"test"u8"ing this out\xFF";
+// expected-error at -1 {{constexpr initializer evaluates to 255 which is not exactly representable in type 'const char'}}
+constexpr unsigned char u8ustring[] = u8"test"u8"ing this out\xFF";
+constexpr unsigned short uustring[] = u"test"u"ing this out\xFF";
+constexpr unsigned int Ustring[] = U"test"U"ing this out\xFF";
+constexpr unsigned char Arr2[6][6] = {
+  {"ek\xFF"}, {"ek\xFF"}
+// expected-error at -1 2{{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+};
+
+constexpr int i = (12);
+constexpr int j = (i);
+constexpr unsigned jneg = (-i);
+// expected-error at -1 {{constexpr initializer evaluates to -12 which is not exactly representable in type 'const unsigned int'}}
+
+// Check that initializer for pointer constexpr variable should be null.
+constexpr int V80 = 3;
+constexpr const int *V81 = &V80;
+// expected-error at -1 {{constexpr pointer initializer is not null}}
+constexpr int *V82 = 0;
+constexpr int *V83 = V82;
+constexpr int *V84 = 42;
+// expected-error at -1 {{constexpr variable 'V84' must be initialized by a constant expression}}
+// expected-note at -2 {{this conversion is not allowed in a constant expression}}
+// expected-error at -3 {{constexpr pointer initializer is not null}}
+constexpr int *V85 = nullptr;
+
+// Check that constexpr variables should not be VLAs.
+void f6(const int P1) {
+  constexpr int V86[P1] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'const int[P1]'}}
+  const int V87 = 3;
+  constexpr int V88[V87] = {};
+// expected-warning at -1 {{variable length array folded to constant array as an extension}}
+  int V89 = 7;
+  constexpr int V90[V89] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'const int[V89]'}}
+}
+
+void f7(int n, int array[n]) {
+  constexpr typeof(array) foo = 0; // Accepted because array is a pointer type, not a VLA type
+  int (*(*fp)(int n))[n];
+  constexpr typeof(fp) bar = 0; // expected-error {{constexpr variable cannot have type 'const typeof (fp)' (aka 'int (*(*const)(int))[n]')}}
+}
+
+// Check how constexpr works with NaNs and infinities.
+#define FLT_NAN __builtin_nanf("1")
+#define DBL_NAN __builtin_nan("1")
+#define LD_NAN __builtin_nanf("1")
+#define FLT_SNAN __builtin_nansf("1")
+#define DBL_SNAN __builtin_nans("1")
+#define LD_SNAN __builtin_nansl("1")
+#define INF __builtin_inf()
+void infsNaNs() {
+  // Inf and quiet NaN is always fine, signaling NaN must have the same type.
+  constexpr float fl0 = INF;
+  constexpr float fl1 = (long double)INF;
+  constexpr float fl2 = (long double)FLT_NAN;
+  constexpr float fl3 = FLT_NAN;
+  constexpr float fl5 = DBL_NAN;
+  constexpr float fl6 = LD_NAN;
+  constexpr float fl7 = DBL_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const float'}}
+  constexpr float fl8 = LD_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const float'}}
+
+  constexpr double db0 = FLT_NAN;
+  constexpr double db2 = DBL_NAN;
+  constexpr double db3 = DBL_SNAN;
+  constexpr double db4 = FLT_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const double'}}
+  constexpr double db5 = LD_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const double'}}
+  constexpr double db6 = INF;
+}

diff  --git a/clang/www/c_status.html b/clang/www/c_status.html
index 3955a1d7963976..7bf5e29f0639cd 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -1201,7 +1201,7 @@ <h2 id="c2x">C23 implementation status</h2>
     <tr>
       <td>constexpr for object definitions</td>
       <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3018.htm">N3018</a></td>
-      <td class="none" align="center">No</td>
+      <td class="unreleased" align="center">Clang 19</td>
     </tr>
     <tr>
       <td>Introduce storage class specifiers for compound literals</td>


        


More information about the cfe-commits mailing list