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

Mariya Podchishchaeva via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 5 03:01:56 PST 2024


https://github.com/Fznamznon updated https://github.com/llvm/llvm-project/pull/73099

>From 1d70b7726e7d1f11622a6d5c8246b0737e024c8d Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Tue, 19 Sep 2023 08:37:18 -0700
Subject: [PATCH 01/14] [C23] Implement N3018: The constexpr specifier for
 object definitions

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.
---
 clang/docs/ReleaseNotes.rst                   |   1 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  16 +
 clang/include/clang/Basic/TokenKinds.def      |   2 +-
 clang/lib/AST/Decl.cpp                        |  16 +-
 clang/lib/AST/ExprConstant.cpp                |  17 +-
 clang/lib/Parse/ParseDecl.cpp                 |   2 +
 clang/lib/Sema/SemaDecl.cpp                   | 204 +++++++++++--
 clang/lib/Sema/SemaOverload.cpp               |  36 ++-
 clang/test/C/C2x/n3018.c                      |  86 ++++++
 clang/test/Parser/c23-constexpr.c             |   6 +
 clang/test/Sema/constexpr.c                   | 275 ++++++++++++++++++
 clang/www/c_status.html                       |   2 +-
 12 files changed, 627 insertions(+), 36 deletions(-)
 create mode 100644 clang/test/C/C2x/n3018.c
 create mode 100644 clang/test/Parser/c23-constexpr.c
 create mode 100644 clang/test/Sema/constexpr.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b65106b9106d4d..cae1707f3e30fe 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -208,6 +208,7 @@ C23 Feature Support
 
 - Clang now supports ``<stdckdint.h>`` which defines several macros for performing
   checked integer arithmetic. It is also exposed in pre-C23 modes.
+- Clang now supports ``N3018 The constexpr specifier for object definitions``.
 
 - Completed the implementation of
   `N2508 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2508.pdf>`_. We
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 990692c06d7d3a..11f24583dc55a9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2932,6 +2932,22 @@ def warn_private_extern : Warning<
 def note_private_extern : Note<
   "use __attribute__((visibility(\"hidden\"))) attribute instead">;
 
+// C23 constexpr
+def err_c23_thread_local_constexpr : Error<
+  "thread-local storage is not allowed with constexpr">;
+def err_c23_extern_constexpr : Error<
+  "extern specifier is not allowed with constexpr">;
+def err_c23_constexpr_not_variable : Error<
+  "constexpr is only allowed 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 3ab420821d82bf..e9e8f59247662e 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 c5c2edf1bfe3ab..678a366ed29ad7 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2461,7 +2461,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.
@@ -2485,12 +2485,12 @@ bool VarDecl::mightBeUsableInConstantExpressions(const ASTContext &C) const {
 
   // In C++, const, non-volatile variables of integral or enumeration types
   // can be used in constant expressions.
-  if (getType()->isIntegralOrEnumerationType())
+  if (getType()->isIntegralOrEnumerationType() && !Lang.C23)
     return true;
 
   // 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 {
@@ -2568,11 +2568,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,
@@ -2630,7 +2630,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 3a41e9718bb587..fdd4bbafbb73c2 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2231,6 +2231,13 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
     return false;
   }
 
+  if (Info.getLangOpts().C23) {
+    auto *VarD = dyn_cast_or_null<VarDecl>(BaseVD);
+    if (VarD && VarD->isConstexpr() && !LVal.isNullPointer()) {
+      Info.report(Loc, diag::err_c23_constexpr_pointer_not_null);
+    }
+  }
+
   // Check that the object is a global. Note that the fake 'this' object we
   // manufacture when checking potential constant expressions is conservatively
   // assumed to be global here.
@@ -4110,6 +4117,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
     }
 
     bool IsConstant = BaseType.isConstant(Info.Ctx);
+    bool ConstexprVar = false;
+    if (const auto *VD = dyn_cast_or_null<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.
@@ -4129,6 +4140,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)
@@ -15704,7 +15718,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 8cb5b09fd3b0fa..7059631515c873 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4168,6 +4168,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/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 4e1857b931cc86..1f74d937bf97ad 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5152,13 +5152,17 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
     // and definitions of functions and variables.
     // C++2a [dcl.constexpr]p1: The consteval specifier shall be applied only to
     // the declaration of a function or function template
-    if (Tag)
+    if (Tag) {
       Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_tag)
           << GetDiagnosticTypeSpecifierID(DS)
           << static_cast<int>(DS.getConstexprSpecifier());
-    else
-      Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_wrong_decl_kind)
-          << 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());
+    }
     // Don't emit warnings after this error.
     return TagD;
   }
@@ -7894,6 +7898,17 @@ NamedDecl *Sema::ActOnVariableDeclarator(
         (getLangOpts().CPlusPlus17 ||
          Context.getTargetInfo().getCXXABI().isMicrosoft()))
       NewVD->setImplicitlyInline();
+
+    if (getLangOpts().C23) {
+      DeclSpec::TSCS TSC = D.getDeclSpec().getThreadStorageClassSpec();
+      if (TSC != TSCS_unspecified) {
+        Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(),
+             diag::err_c23_thread_local_constexpr);
+      }
+      if (NewVD->hasExternalStorage())
+        Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(),
+             diag::err_c23_extern_constexpr);
+    }
     break;
 
   case ConstexprSpecKind::Constinit:
@@ -8605,6 +8620,27 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND,
   return false;
 }
 
+static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef,
+                                            SourceLocation VarLoc, QualType T) {
+  if (const auto *A = SemaRef.Context.getAsArrayType(T)) {
+    T = A->getElementType();
+  }
+
+  if (T->isAtomicType() || T.isVolatileQualified() || T.isRestrictQualified()) {
+    SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
+    return true;
+  }
+
+  if (T->isRecordType()) {
+    RecordDecl *RD = T->getAsRecordDecl();
+    for (const auto &F : RD->fields())
+      if (CheckC23ConstexprVarTypeQualifiers(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())
@@ -8857,7 +8893,9 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
 
   if (NewVD->isConstexpr() && !T->isDependentType() &&
       RequireLiteralType(NewVD->getLocation(), T,
-                         diag::err_constexpr_var_non_literal)) {
+                         getLangOpts().C23
+                             ? diag::err_c23_constexpr_invalid_type
+                             : diag::err_constexpr_var_non_literal)) {
     NewVD->setInvalidDecl();
     return;
   }
@@ -8870,6 +8908,12 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
     return;
   }
 
+  if (getLangOpts().C23 && NewVD->isConstexpr() &&
+      CheckC23ConstexprVarTypeQualifiers(*this, NewVD->getLocation(), T)) {
+    NewVD->setInvalidDecl();
+    return;
+  }
+
   // Check that SVE types are only used in functions with SVE available.
   if (T->isSVESizelessBuiltinType() && isa<FunctionDecl>(CurContext)) {
     const FunctionDecl *FD = cast<FunctionDecl>(CurContext);
@@ -9237,6 +9281,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:
@@ -9270,15 +9330,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);
@@ -13786,7 +13837,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 double diagnosing for constexpr variables.
+    if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl() &&
+        !VDecl->isConstexpr())
       CheckForConstantInitializer(Init, DclT);
   }
 
@@ -14240,6 +14293,115 @@ StmtResult Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
                                                       : IdentLoc);
 }
 
+static ImplicitConversionKind getConversionKind(QualType FromType,
+                                                QualType ToType) {
+  if (ToType->isIntegerType()) {
+    if (FromType->isComplexType())
+     return ICK_Complex_Real;
+    if (FromType->isFloatingType())
+     return ICK_Floating_Integral;
+    if (FromType->isIntegerType())
+     return ICK_Integral_Conversion;
+  }
+
+  if (ToType->isFloatingType()) {
+    if (FromType->isComplexType())
+     return ICK_Complex_Real;
+    if (FromType->isFloatingType())
+     return ICK_Floating_Conversion;
+    if (FromType->isIntegerType())
+     return ICK_Floating_Integral;
+  }
+
+  return ICK_Identity;
+}
+
+static bool checkC23ConstexprInitConversion(Sema &S, const Expr *Init) {
+  assert(S.getLangOpts().C23);
+  const Expr *InitNoCast = Init->IgnoreImpCasts();
+  StandardConversionSequence SCS;
+  SCS.setAsIdentityConversion();
+  auto FromType = InitNoCast->getType();
+  auto ToType = Init->getType();
+  SCS.setToType(0, FromType);
+  SCS.setToType(1, ToType);
+  SCS.Second = getConversionKind(FromType, ToType);
+
+  APValue Value;
+  QualType PreNarrowingType;
+  // Reuse C++ narrowing check.
+  switch (SCS.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 true;
+
+  // Conversion to a narrower type.
+  case NK_Type_Narrowing:
+    S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_type_mismatch)
+        << ToType << FromType;
+    return true;
+
+  // 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 false;
+
+  }
+  llvm_unreachable("unhandled case in switch");
+}
+
+static bool checkC23ConstexprInitStringLiteral(const StringLiteral *SE,
+                                               Sema &SemaRef,
+                                               SourceLocation Loc) {
+  assert(SemaRef.getLangOpts().C23);
+  // String literals have the target type attached but underneath may contain
+  // values that don't really fit into the target type. Check that every
+  // character fits.
+  const ConstantArrayType *CAT =
+      SemaRef.Context.getAsConstantArrayType(SE->getType());
+  QualType CharType = CAT->getElementType();
+  uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType);
+  bool isUnsigned = CharType->isUnsignedIntegerType();
+  llvm::APSInt Value(BitWidth, isUnsigned);
+  const StringRef S = SE->getBytes();
+  for (unsigned I = 0, N = SE->getLength(); I != N; ++I) {
+    Value = S[I];
+    if (Value != S[I]) {
+      SemaRef.Diag(Loc, diag::err_c23_constexpr_init_not_representable)
+          << S[I] << CharType;
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool checkC23ConstexprInitializer(Sema &S, const Expr *Init) {
+  const Expr *InitNoCast = Init->IgnoreImpCasts();
+  if (Init->getType() != InitNoCast->getType())
+    if (checkC23ConstexprInitConversion(S, Init))
+      return true;
+
+  if (auto *SE = dyn_cast<StringLiteral>(Init))
+    if (checkC23ConstexprInitStringLiteral(SE, S, Init->getBeginLoc()))
+      return true;
+
+  for (const Stmt *SubStmt : Init->children()) {
+    const Expr *ChildExpr = dyn_cast_or_null<const Expr>(SubStmt);
+    if (!ChildExpr)
+      continue;
+
+    if (checkC23ConstexprInitializer(S, ChildExpr)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
   if (var->isInvalidDecl()) return;
 
@@ -14397,9 +14559,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) &&
+      !type->isDependentType() && Init && !Init->isValueDependent() &&
       (GlobalStorage || var->isConstexpr() ||
        var->mightBeUsableInConstantExpressions(Context))) {
     // If this variable might have a constant initializer or might be usable in
@@ -14407,7 +14573,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.
@@ -14432,6 +14598,8 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
 
     if (HasConstInit) {
       // FIXME: Consider replacing the initializer with a ConstantExpr.
+      if (getLangOpts().C23 && var->isConstexpr())
+        checkC23ConstexprInitializer(*this, Init);
     } else if (var->isConstexpr()) {
       SourceLocation DiagLoc = var->getLocation();
       // If the note doesn't add any useful information other than a source
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 64607e28b8b35e..dfb1dfc00c1bc4 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -328,7 +328,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 ...
@@ -410,20 +411,35 @@ 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(
+        llvm::APFloat Converted = FloatVal;
+        llvm::APFloat::opStatus ConvertStatus = Converted.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;
+        Converted.convert(Ctx.getFloatTypeSemantics(FromType),
+                          llvm::APFloat::rmNearestTiesToEven, &ignored);
+        if (Ctx.getLangOpts().C23) {
+          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;
@@ -490,6 +506,10 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
     }
     return NK_Not_Narrowing;
   }
+  case ICK_Complex_Real:
+    if (FromType->isComplexType() && !ToType->isComplexType())
+      return NK_Type_Narrowing;
+    return NK_Not_Narrowing;
 
   default:
     // Other kinds of conversions are not narrowings.
diff --git a/clang/test/C/C2x/n3018.c b/clang/test/C/C2x/n3018.c
new file mode 100644
index 00000000000000..ccc0b708a67e06
--- /dev/null
+++ b/clang/test/C/C2x/n3018.c
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -std=c2x -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 '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 '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 '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 '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 'unsigned short'}}
+      -1       // constraint violation, target type is unsigned
+  };
+
+  constexpr static unsigned short array1[] = {
+      3000,
+      3000,
+      -1
+       // expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type '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..5eddf66a66e74b
--- /dev/null
+++ b/clang/test/Parser/c23-constexpr.c
@@ -0,0 +1,6 @@
+// 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}}
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
new file mode 100644
index 00000000000000..650716640e2f47
--- /dev/null
+++ b/clang/test/Sema/constexpr.c
@@ -0,0 +1,275 @@
+// RUN: %clang_cc1 -std=c2x -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 is only allowed in variable declarations}}
+constexpr const int f1() { return 0; } // expected-error {{constexpr is only allowed 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 is only allowed 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;
+
+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 {{thread-local storage is not allowed with constexpr}}
+constexpr static thread_local double V12 = 38; // expected-error {{thread-local storage is not allowed with constexpr}}
+constexpr extern thread_local char V13; // expected-error {{extern specifier is not allowed with constexpr}}
+// expected-error at -1 {{thread-local storage is not allowed with constexpr}}
+// expected-error at -2 {{constexpr variable declaration must be a definition}}
+constexpr thread_local short V14 = 38; // expected-error {{thread-local storage is not allowed with constexpr}}
+
+// 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'}}
+
+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' (aka 'const _Atomic(int)')}}
+constexpr TheV V20[3] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'const TheV' (aka 'const volatile short')}}
+constexpr TheR V21[3] = {};
+// expected-error at -1 {{constexpr variable cannot have type 'const TheR' (aka 'float *const restrict')}}
+
+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;
+
+// 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}}
+// expected-error at -2 {{constexpr pointer initializer is not null}}
+}
+
+// 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;
+};
+
+void f5() {
+  constexpr char V50 = 300;
+  // expected-error at -1 {{constexpr initializer evaluates to 300 which is not exactly representable in type '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 'float'}}
+  constexpr float V52 = 0.7;
+  // expected-error at -1 {{constexpr initializer evaluates to 7.000000e-01 which is not exactly representable in type '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 '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'}}
+  };
+
+  // FIXME Shouldn't be diagnosed if char_8t is supported.
+  constexpr unsigned char V56[] = {
+      u8"\xAF",
+  // expected-error at -1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
+  };
+  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 'float'}}
+
+  constexpr unsigned char V64 = '\xAF';
+  // expected-error at -1 {{constexpr initializer evaluates to -81 which is not exactly representable in type '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 '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'}}
+  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 'int' is of type 'double'}}
+  constexpr int V72 = V59;
+  // expected-error at -1 {{constexpr initializer for type '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 'unsigned int' is of type 'double'}}
+
+  constexpr _Complex float V77 = 0;
+  constexpr float V78 = V77;
+  // expected-error at -1 {{constexpr initializer for type 'float' is of type 'const _Complex float'}}
+  constexpr int V79 = V77;
+  // expected-error at -1 {{constexpr initializer for type 'int' is of type 'const _Complex float'}}
+
+}
+
+// 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}}
+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]'}}
+}
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 91cae138074b33..4644d18b496173 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 18</td>
     </tr>
     <tr>
       <td>Introduce storage class specifiers for compound literals</td>

>From bbb801dde58ecd336369985d146b2592d5748593 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Thu, 23 Nov 2023 06:44:12 -0800
Subject: [PATCH 02/14] Fix format, apply comments

---
 clang/lib/AST/ExprConstant.cpp  |  7 +++----
 clang/lib/Sema/SemaDecl.cpp     | 18 +++++++++---------
 clang/lib/Sema/SemaOverload.cpp |  9 ++++-----
 3 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index fdd4bbafbb73c2..97827e31d8f4ab 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2232,10 +2232,9 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
   }
 
   if (Info.getLangOpts().C23) {
-    auto *VarD = dyn_cast_or_null<VarDecl>(BaseVD);
-    if (VarD && VarD->isConstexpr() && !LVal.isNullPointer()) {
+    if (const auto *VarD = dyn_cast_if_present<VarDecl>(BaseVD);
+        VarD && VarD->isConstexpr() && !LVal.isNullPointer())
       Info.report(Loc, diag::err_c23_constexpr_pointer_not_null);
-    }
   }
 
   // Check that the object is a global. Note that the fake 'this' object we
@@ -4118,7 +4117,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
 
     bool IsConstant = BaseType.isConstant(Info.Ctx);
     bool ConstexprVar = false;
-    if (const auto *VD = dyn_cast_or_null<VarDecl>(
+    if (const auto *VD = dyn_cast_if_present<VarDecl>(
             Info.EvaluatingDecl.dyn_cast<const ValueDecl *>()))
       ConstexprVar = VD->isConstexpr();
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 1f74d937bf97ad..1ffc9971ae6984 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8621,7 +8621,8 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND,
 }
 
 static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef,
-                                            SourceLocation VarLoc, QualType T) {
+                                               SourceLocation VarLoc,
+                                               QualType T) {
   if (const auto *A = SemaRef.Context.getAsArrayType(T)) {
     T = A->getElementType();
   }
@@ -8632,7 +8633,7 @@ static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef,
   }
 
   if (T->isRecordType()) {
-    RecordDecl *RD = T->getAsRecordDecl();
+    const RecordDecl *RD = T->getAsRecordDecl();
     for (const auto &F : RD->fields())
       if (CheckC23ConstexprVarTypeQualifiers(SemaRef, VarLoc, F->getType()))
         return true;
@@ -14297,20 +14298,20 @@ static ImplicitConversionKind getConversionKind(QualType FromType,
                                                 QualType ToType) {
   if (ToType->isIntegerType()) {
     if (FromType->isComplexType())
-     return ICK_Complex_Real;
+      return ICK_Complex_Real;
     if (FromType->isFloatingType())
-     return ICK_Floating_Integral;
+      return ICK_Floating_Integral;
     if (FromType->isIntegerType())
-     return ICK_Integral_Conversion;
+      return ICK_Integral_Conversion;
   }
 
   if (ToType->isFloatingType()) {
     if (FromType->isComplexType())
-     return ICK_Complex_Real;
+      return ICK_Complex_Real;
     if (FromType->isFloatingType())
-     return ICK_Floating_Conversion;
+      return ICK_Floating_Conversion;
     if (FromType->isIntegerType())
-     return ICK_Floating_Integral;
+      return ICK_Floating_Integral;
   }
 
   return ICK_Identity;
@@ -14350,7 +14351,6 @@ static bool checkC23ConstexprInitConversion(Sema &S, const Expr *Init) {
   case NK_Variable_Narrowing:
   case NK_Not_Narrowing:
     return false;
-
   }
   llvm_unreachable("unhandled case in switch");
 }
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index dfb1dfc00c1bc4..0d3a9701b9501b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -412,8 +412,7 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
         return NK_Dependent_Narrowing;
 
       Expr::EvalResult R;
-      if ((Ctx.getLangOpts().C23 &&
-           Initializer->EvaluateAsRValue(R, Ctx)) ||
+      if ((Ctx.getLangOpts().C23 && Initializer->EvaluateAsRValue(R, Ctx)) ||
           Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) {
         // Constant!
         if (Ctx.getLangOpts().C23)
@@ -423,9 +422,9 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
         // Convert the source value into the target type.
         bool ignored;
         llvm::APFloat Converted = FloatVal;
-        llvm::APFloat::opStatus ConvertStatus = Converted.convert(
-          Ctx.getFloatTypeSemantics(ToType),
-          llvm::APFloat::rmNearestTiesToEven, &ignored);
+        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) {

>From 9b4a5ea626138cc110c09ff41fb0793d9fb2a02b Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Wed, 29 Nov 2023 09:18:51 -0800
Subject: [PATCH 03/14] Apply some of the feedback:

- Minor changes
- Don't reuse requireLiteralType
- Diagnose interaction with extern and thread_local from
  DeclSpec::Finish()
- Add a couple of test cases
---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +-
 clang/lib/AST/ExprConstant.cpp                |  4 +
 clang/lib/Sema/DeclSpec.cpp                   | 14 +++
 clang/lib/Sema/SemaDecl.cpp                   | 93 +++++++++----------
 clang/test/Sema/constexpr.c                   | 26 ++++--
 5 files changed, 80 insertions(+), 59 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1841f0f44fb0ab..016a099f012938 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2938,7 +2938,7 @@ def err_c23_thread_local_constexpr : Error<
 def err_c23_extern_constexpr : Error<
   "extern specifier is not allowed with constexpr">;
 def err_c23_constexpr_not_variable : Error<
-  "constexpr is only allowed in variable declarations">;
+  "'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<
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 06f1635b5bd0fe..bfea4414bc544c 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2231,6 +2231,10 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
     return false;
   }
 
+  // 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.
   if (Info.getLangOpts().C23) {
     if (const auto *VarD = dyn_cast_if_present<VarDecl>(BaseVD);
         VarD && VarD->isConstexpr() && !LVal.isNullPointer())
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 781f24cb71ae99..a3cf215d8a64aa 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1361,6 +1361,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 6aff091a1df3f8..9ee69880d1e36a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5172,17 +5172,15 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
     // and definitions of functions and variables.
     // C++2a [dcl.constexpr]p1: The consteval specifier shall be applied only to
     // the declaration of a function or function template
-    if (Tag) {
+    if (Tag)
       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());
-    }
+    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());
     // Don't emit warnings after this error.
     return TagD;
   }
@@ -7918,17 +7916,6 @@ NamedDecl *Sema::ActOnVariableDeclarator(
         (getLangOpts().CPlusPlus17 ||
          Context.getTargetInfo().getCXXABI().isMicrosoft()))
       NewVD->setImplicitlyInline();
-
-    if (getLangOpts().C23) {
-      DeclSpec::TSCS TSC = D.getDeclSpec().getThreadStorageClassSpec();
-      if (TSC != TSCS_unspecified) {
-        Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(),
-             diag::err_c23_thread_local_constexpr);
-      }
-      if (NewVD->hasExternalStorage())
-        Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(),
-             diag::err_c23_extern_constexpr);
-    }
     break;
 
   case ConstexprSpecKind::Constinit:
@@ -8640,13 +8627,21 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND,
   return false;
 }
 
-static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef,
-                                               SourceLocation VarLoc,
-                                               QualType T) {
-  if (const auto *A = SemaRef.Context.getAsArrayType(T)) {
-    T = A->getElementType();
+static bool CheckC23ConstexprVarType(Sema &SemaRef, SourceLocation VarLoc,
+                                     QualType T) {
+
+  if (T->isVariableArrayType()) {
+    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).
+  T = SemaRef.Context.getBaseElementType(T);
+
+  // C23 6.7.1p4: 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 (T->isAtomicType() || T.isVolatileQualified() || T.isRestrictQualified()) {
     SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
     return true;
@@ -8654,9 +8649,10 @@ static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef,
 
   if (T->isRecordType()) {
     const RecordDecl *RD = T->getAsRecordDecl();
-    for (const auto &F : RD->fields())
-      if (CheckC23ConstexprVarTypeQualifiers(SemaRef, VarLoc, F->getType()))
-        return true;
+    if (llvm::any_of(RD->fields(), [&SemaRef, VarLoc](const FieldDecl *F) {
+          return CheckC23ConstexprVarType(SemaRef, VarLoc, F->getType());
+        }))
+      return true;
   }
 
   return false;
@@ -8912,11 +8908,15 @@ 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,
-                         getLangOpts().C23
-                             ? diag::err_c23_constexpr_invalid_type
-                             : diag::err_constexpr_var_non_literal)) {
+                         diag::err_constexpr_var_non_literal)) {
     NewVD->setInvalidDecl();
     return;
   }
@@ -8929,12 +8929,6 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
     return;
   }
 
-  if (getLangOpts().C23 && NewVD->isConstexpr() &&
-      CheckC23ConstexprVarTypeQualifiers(*this, NewVD->getLocation(), T)) {
-    NewVD->setInvalidDecl();
-    return;
-  }
-
   // Check that SVE types are only used in functions with SVE available.
   if (T->isSVESizelessBuiltinType() && isa<FunctionDecl>(CurContext)) {
     const FunctionDecl *FD = cast<FunctionDecl>(CurContext);
@@ -14314,7 +14308,7 @@ StmtResult Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
                                                       : IdentLoc);
 }
 
-static ImplicitConversionKind getConversionKind(QualType FromType,
+static ImplicitConversionKind GetConversionKind(QualType FromType,
                                                 QualType ToType) {
   if (ToType->isIntegerType()) {
     if (FromType->isComplexType())
@@ -14337,16 +14331,16 @@ static ImplicitConversionKind getConversionKind(QualType FromType,
   return ICK_Identity;
 }
 
-static bool checkC23ConstexprInitConversion(Sema &S, const Expr *Init) {
+static bool CheckC23ConstexprInitConversion(Sema &S, const Expr *Init) {
   assert(S.getLangOpts().C23);
-  const Expr *InitNoCast = Init->IgnoreImpCasts();
+  const Expr *InitNoCast = Init->IgnoreParenImpCasts();
   StandardConversionSequence SCS;
   SCS.setAsIdentityConversion();
   auto FromType = InitNoCast->getType();
   auto ToType = Init->getType();
   SCS.setToType(0, FromType);
   SCS.setToType(1, ToType);
-  SCS.Second = getConversionKind(FromType, ToType);
+  SCS.Second = GetConversionKind(FromType, ToType);
 
   APValue Value;
   QualType PreNarrowingType;
@@ -14375,7 +14369,7 @@ static bool checkC23ConstexprInitConversion(Sema &S, const Expr *Init) {
   llvm_unreachable("unhandled case in switch");
 }
 
-static bool checkC23ConstexprInitStringLiteral(const StringLiteral *SE,
+static bool CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
                                                Sema &SemaRef,
                                                SourceLocation Loc) {
   assert(SemaRef.getLangOpts().C23);
@@ -14400,24 +14394,23 @@ static bool checkC23ConstexprInitStringLiteral(const StringLiteral *SE,
   return false;
 }
 
-static bool checkC23ConstexprInitializer(Sema &S, const Expr *Init) {
-  const Expr *InitNoCast = Init->IgnoreImpCasts();
+static bool CheckC23ConstexprInitializer(Sema &S, const Expr *Init) {
+  const Expr *InitNoCast = Init->IgnoreParenImpCasts();
   if (Init->getType() != InitNoCast->getType())
-    if (checkC23ConstexprInitConversion(S, Init))
+    if (CheckC23ConstexprInitConversion(S, Init))
       return true;
 
-  if (auto *SE = dyn_cast<StringLiteral>(Init))
-    if (checkC23ConstexprInitStringLiteral(SE, S, Init->getBeginLoc()))
+  if (const auto *SE = dyn_cast<StringLiteral>(Init))
+    if (CheckC23ConstexprInitStringLiteral(SE, S, Init->getBeginLoc()))
       return true;
 
   for (const Stmt *SubStmt : Init->children()) {
-    const Expr *ChildExpr = dyn_cast_or_null<const Expr>(SubStmt);
+    const Expr *ChildExpr = dyn_cast_or_null<Expr>(SubStmt);
     if (!ChildExpr)
       continue;
 
-    if (checkC23ConstexprInitializer(S, ChildExpr)) {
+    if (CheckC23ConstexprInitializer(S, ChildExpr))
       return true;
-    }
   }
   return false;
 }
@@ -14619,7 +14612,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
     if (HasConstInit) {
       // FIXME: Consider replacing the initializer with a ConstantExpr.
       if (getLangOpts().C23 && var->isConstexpr())
-        checkC23ConstexprInitializer(*this, Init);
+        CheckC23ConstexprInitializer(*this, Init);
     } else if (var->isConstexpr()) {
       SourceLocation DiagLoc = var->getLocation();
       // If the note doesn't add any useful information other than a source
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index 650716640e2f47..d8e68b56f918ce 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -1,8 +1,8 @@
 // RUN: %clang_cc1 -std=c2x -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 is only allowed in variable declarations}}
-constexpr const int f1() { return 0; } // expected-error {{constexpr is only allowed in variable declarations}}
+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}}
@@ -18,7 +18,7 @@ struct S3 {
   // expected-error at -2 {{expected ';' at end}}
 };
 
-constexpr; // expected-error {{constexpr is only allowed in variable declarations}}
+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}}
@@ -40,12 +40,12 @@ void f3(constexpr register int P1) { // expected-error {{function parameter cann
   constexpr register auto V10 = 0.0;
 }
 
-constexpr thread_local int V11 = 38; // expected-error {{thread-local storage is not allowed with constexpr}}
-constexpr static thread_local double V12 = 38; // expected-error {{thread-local storage is not allowed with constexpr}}
-constexpr extern thread_local char V13; // expected-error {{extern specifier is not allowed with constexpr}}
-// expected-error at -1 {{thread-local storage is not allowed with constexpr}}
+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 {{thread-local storage is not allowed with constexpr}}
+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)'}}
@@ -251,6 +251,16 @@ void f5() {
 
 }
 
+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 wstring[] = u8"test"u8"ing this out\xFF";
+constexpr unsigned char wustring[] = u8"test"u8"ing this out\xFF";
+// expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+
+constexpr int i = (12);
+constexpr int j = (i);
+
 // Check that initializer for pointer constexpr variable should be null.
 constexpr int V80 = 3;
 constexpr const int *V81 = &V80;

>From d14018a89eee8cf549b4e2530b9ce4d2f51b27c5 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Wed, 29 Nov 2023 09:23:19 -0800
Subject: [PATCH 04/14] Update a release note

---
 clang/docs/ReleaseNotes.rst | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 55822d55b5b4f1..a2c377d5f0a5dc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -208,7 +208,8 @@ C23 Feature Support
 
 - Clang now supports ``<stdckdint.h>`` which defines several macros for performing
   checked integer arithmetic. It is also exposed in pre-C23 modes.
-- Clang now supports ``N3018 The constexpr specifier for object definitions``.
+- Clang now supports `N3018 The constexpr specifier for object definitions`
+  <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3018.htm>`_.
 
 - Completed the implementation of
   `N2508 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2508.pdf>`_. We

>From 85ad720fb8bd78d0e72625557e2a26d9647e74eb Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Wed, 29 Nov 2023 09:28:05 -0800
Subject: [PATCH 05/14] Remove unused messages

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 016a099f012938..dc5e58fc4683a4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2933,10 +2933,6 @@ def note_private_extern : Note<
   "use __attribute__((visibility(\"hidden\"))) attribute instead">;
 
 // C23 constexpr
-def err_c23_thread_local_constexpr : Error<
-  "thread-local storage is not allowed with constexpr">;
-def err_c23_extern_constexpr : Error<
-  "extern specifier is not allowed with constexpr">;
 def err_c23_constexpr_not_variable : Error<
   "'constexpr' can only be used in variable declarations">;
 def err_c23_constexpr_invalid_type : Error<

>From 83d96777a93c38a91fef189b31269f5f6674d671 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Thu, 30 Nov 2023 03:07:49 -0800
Subject: [PATCH 06/14] Update comments

---
 clang/lib/AST/Decl.cpp | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 678a366ed29ad7..ce91f396081b15 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2483,11 +2483,16 @@ 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.
+  // 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 || Lang.C23) && isConstexpr();

>From 0d24ada6f485727f4c311d094381c7a405fd3cbb Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Mon, 4 Dec 2023 05:44:15 -0800
Subject: [PATCH 07/14] Use getCodeUnit

---
 clang/include/clang/AST/Expr.h | 11 +++++++++++
 clang/lib/Sema/SemaDecl.cpp    |  8 ++++----
 clang/test/Sema/constexpr.c    | 12 +++++++-----
 3 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index a9c4c67a60e8e8..885df074f9be20 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -1914,6 +1914,17 @@ class StringLiteral final
     llvm_unreachable("Unsupported character width!");
   }
 
+  // Get code unit but preserve sign info.
+  int64_t getCodeUnitS(size_t I, uint64_t ByteWidth) const {
+    int64_t V = getCodeUnit(I);
+    if (isOrdinary() || isWide()) {
+      unsigned Width = getCharByteWidth() * ByteWidth;
+      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/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 744e18747dbae6..1811e4b8c6ce74 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14392,12 +14392,12 @@ static bool CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
   uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType);
   bool isUnsigned = CharType->isUnsignedIntegerType();
   llvm::APSInt Value(BitWidth, isUnsigned);
-  const StringRef S = SE->getBytes();
   for (unsigned I = 0, N = SE->getLength(); I != N; ++I) {
-    Value = S[I];
-    if (Value != S[I]) {
+    int64_t C = SE->getCodeUnitS(I, SemaRef.Context.getCharWidth());
+    Value = C;
+    if (Value != C) {
       SemaRef.Diag(Loc, diag::err_c23_constexpr_init_not_representable)
-          << S[I] << CharType;
+          << C << CharType;
       return true;
     }
   }
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index d8e68b56f918ce..ba5168b8e2a771 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -199,10 +199,8 @@ void f5() {
   // expected-error at -1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
   };
 
-  // FIXME Shouldn't be diagnosed if char_8t is supported.
   constexpr unsigned char V56[] = {
       u8"\xAF",
-  // expected-error at -1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
   };
   constexpr struct S6 V57 = {299};
   // expected-error at -1 {{constexpr initializer evaluates to 299 which is not exactly representable in type 'unsigned char'}}
@@ -254,12 +252,16 @@ void f5() {
 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 wstring[] = u8"test"u8"ing this out\xFF";
-constexpr unsigned char wustring[] = u8"test"u8"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 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 'unsigned int'}}
 
 // Check that initializer for pointer constexpr variable should be null.
 constexpr int V80 = 3;

>From 6b740ae324730e014246a4a330c493e05517c3d4 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Wed, 6 Dec 2023 03:32:55 -0800
Subject: [PATCH 08/14] Apply suggestions

---
 clang/include/clang/AST/Expr.h |  4 ++--
 clang/lib/Sema/SemaDecl.cpp    | 10 +++++-----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 885df074f9be20..ddef339054c44e 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -1915,10 +1915,10 @@ class StringLiteral final
   }
 
   // Get code unit but preserve sign info.
-  int64_t getCodeUnitS(size_t I, uint64_t ByteWidth) const {
+  int64_t getCodeUnitS(size_t I, uint64_t BitWidth) const {
     int64_t V = getCodeUnit(I);
     if (isOrdinary() || isWide()) {
-      unsigned Width = getCharByteWidth() * ByteWidth;
+      unsigned Width = getCharByteWidth() * BitWidth;
       llvm::APInt AInt(Width, (uint64_t)V);
       V = AInt.getSExtValue();
     }
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a90b0ecd015b47..f84f10a63027d5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13863,7 +13863,7 @@ 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.
-    // Avoid double diagnosing for constexpr variables.
+    // Avoid double diagnoses for constexpr variables.
     if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl() &&
         !VDecl->isConstexpr())
       CheckForConstantInitializer(Init, DclT);
@@ -14381,8 +14381,7 @@ static bool CheckC23ConstexprInitConversion(Sema &S, const Expr *Init) {
 }
 
 static bool CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
-                                               Sema &SemaRef,
-                                               SourceLocation Loc) {
+                                               Sema &SemaRef) {
   assert(SemaRef.getLangOpts().C23);
   // String literals have the target type attached but underneath may contain
   // values that don't really fit into the target type. Check that every
@@ -14397,7 +14396,8 @@ static bool CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
     int64_t C = SE->getCodeUnitS(I, SemaRef.Context.getCharWidth());
     Value = C;
     if (Value != C) {
-      SemaRef.Diag(Loc, diag::err_c23_constexpr_init_not_representable)
+      SemaRef.Diag(SemaRef.getLocationOfStringLiteralByte(SE, I),
+                   diag::err_c23_constexpr_init_not_representable)
           << C << CharType;
       return true;
     }
@@ -14412,7 +14412,7 @@ static bool CheckC23ConstexprInitializer(Sema &S, const Expr *Init) {
       return true;
 
   if (const auto *SE = dyn_cast<StringLiteral>(Init))
-    if (CheckC23ConstexprInitStringLiteral(SE, S, Init->getBeginLoc()))
+    if (CheckC23ConstexprInitStringLiteral(SE, S))
       return true;
 
   for (const Stmt *SubStmt : Init->children()) {

>From 40f7d86f82be7f7308b691ac7f2d49cad024e1ff Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Tue, 12 Dec 2023 02:36:03 -0800
Subject: [PATCH 09/14] Add test improvement

---
 clang/test/Sema/constexpr.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index ba5168b8e2a771..e247773d98d127 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -34,6 +34,15 @@ 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";
+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));
 
 void f3(constexpr register int P1) { // expected-error {{function parameter cannot be constexpr}}
   constexpr register int V9 = 0;

>From bc0745c3da4a59ade6ea3ae4d55ad309d62781b6 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Fri, 2 Feb 2024 06:04:58 -0800
Subject: [PATCH 10/14] Target clang 19

---
 clang/www/c_status.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index f0644235dc5c3a..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="unreleased" align="center">Clang 18</td>
+      <td class="unreleased" align="center">Clang 19</td>
     </tr>
     <tr>
       <td>Introduce storage class specifiers for compound literals</td>

>From cdec1b775c270498d6b3df26964c3c68bd873bfe Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Tue, 20 Feb 2024 07:10:05 -0800
Subject: [PATCH 11/14] Evaluate initializers in C++ fashion only for C23
 constexpr vars

Caused test fail in test/Sema/atomic-expr.c with the new evaluator.
---
 clang/lib/Sema/SemaDecl.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 6b5051c451dcb9..332f62d5ac994e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14653,7 +14653,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
         << var;
 
   // Check whether the initializer is sufficiently constant.
-  if ((getLangOpts().CPlusPlus || getLangOpts().C23) &&
+  if ((getLangOpts().CPlusPlus || (getLangOpts().C23 && var->isConstexpr())) &&
       !type->isDependentType() && Init && !Init->isValueDependent() &&
       (GlobalStorage || var->isConstexpr() ||
        var->mightBeUsableInConstantExpressions(Context))) {

>From 10ef7df6e6c826db12b5b1cc13647f4a2f3af37c Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Mon, 26 Feb 2024 08:56:42 -0800
Subject: [PATCH 12/14] Apply review feedback

- Move initializer checking to SemaInit
- Use TryImplicitSequence
- Fix null pointer initializer check, it was all wrong
- When doing type check, check canonical type
- Add test cases
---
 clang/lib/AST/ExprConstant.cpp    |  10 ---
 clang/lib/Sema/SemaDecl.cpp       | 130 +++---------------------------
 clang/lib/Sema/SemaInit.cpp       | 119 ++++++++++++++++++++++++++-
 clang/test/C/C2x/n3018.c          |  15 ++--
 clang/test/Parser/c23-constexpr.c |  11 +++
 clang/test/Sema/constexpr.c       |  74 ++++++++++++-----
 6 files changed, 200 insertions(+), 159 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index e46cc9c1a54cca..ac7cf7f42a19db 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2252,16 +2252,6 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
     return false;
   }
 
-  // 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.
-  if (Info.getLangOpts().C23) {
-    if (const auto *VarD = dyn_cast_if_present<VarDecl>(BaseVD);
-        VarD && VarD->isConstexpr() && !LVal.isNullPointer())
-      Info.report(Loc, diag::err_c23_constexpr_pointer_not_null);
-  }
-
   // Check that the object is a global. Note that the fake 'this' object we
   // manufacture when checking potential constant expressions is conservatively
   // assumed to be global here.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 332f62d5ac994e..e50ab49b5832e6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8626,26 +8626,27 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND,
 
 static bool CheckC23ConstexprVarType(Sema &SemaRef, SourceLocation VarLoc,
                                      QualType T) {
-
-  if (T->isVariableArrayType()) {
+  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).
-  T = SemaRef.Context.getBaseElementType(T);
+  CanonT = SemaRef.Context.getBaseElementType(CanonT);
 
-  // C23 6.7.1p4: 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 (T->isAtomicType() || T.isVolatileQualified() || T.isRestrictQualified()) {
+  if (CanonT->isAtomicType() || CanonT.isVolatileQualified() ||
+      CanonT.isRestrictQualified()) {
     SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
     return true;
   }
 
-  if (T->isRecordType()) {
-    const RecordDecl *RD = T->getAsRecordDecl();
+  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());
         }))
@@ -13928,7 +13929,7 @@ 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.
-    // Avoid double diagnoses for constexpr variables.
+    // Avoid duplicate diagnostics for constexpr variables.
     if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl() &&
         !VDecl->isConstexpr())
       CheckForConstantInitializer(Init, DclT);
@@ -14384,113 +14385,6 @@ StmtResult Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
                                                       : IdentLoc);
 }
 
-static ImplicitConversionKind GetConversionKind(QualType FromType,
-                                                QualType ToType) {
-  if (ToType->isIntegerType()) {
-    if (FromType->isComplexType())
-      return ICK_Complex_Real;
-    if (FromType->isFloatingType())
-      return ICK_Floating_Integral;
-    if (FromType->isIntegerType())
-      return ICK_Integral_Conversion;
-  }
-
-  if (ToType->isFloatingType()) {
-    if (FromType->isComplexType())
-      return ICK_Complex_Real;
-    if (FromType->isFloatingType())
-      return ICK_Floating_Conversion;
-    if (FromType->isIntegerType())
-      return ICK_Floating_Integral;
-  }
-
-  return ICK_Identity;
-}
-
-static bool CheckC23ConstexprInitConversion(Sema &S, const Expr *Init) {
-  assert(S.getLangOpts().C23);
-  const Expr *InitNoCast = Init->IgnoreParenImpCasts();
-  StandardConversionSequence SCS;
-  SCS.setAsIdentityConversion();
-  auto FromType = InitNoCast->getType();
-  auto ToType = Init->getType();
-  SCS.setToType(0, FromType);
-  SCS.setToType(1, ToType);
-  SCS.Second = GetConversionKind(FromType, ToType);
-
-  APValue Value;
-  QualType PreNarrowingType;
-  // Reuse C++ narrowing check.
-  switch (SCS.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 true;
-
-  // Conversion to a narrower type.
-  case NK_Type_Narrowing:
-    S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_type_mismatch)
-        << ToType << FromType;
-    return true;
-
-  // 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 false;
-  }
-  llvm_unreachable("unhandled case in switch");
-}
-
-static bool CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
-                                               Sema &SemaRef) {
-  assert(SemaRef.getLangOpts().C23);
-  // String literals have the target type attached but underneath may contain
-  // values that don't really fit into the target type. Check that every
-  // character fits.
-  const ConstantArrayType *CAT =
-      SemaRef.Context.getAsConstantArrayType(SE->getType());
-  QualType CharType = CAT->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 true;
-    }
-  }
-  return false;
-}
-
-static bool CheckC23ConstexprInitializer(Sema &S, const Expr *Init) {
-  const Expr *InitNoCast = Init->IgnoreParenImpCasts();
-  if (Init->getType() != InitNoCast->getType())
-    if (CheckC23ConstexprInitConversion(S, Init))
-      return true;
-
-  if (const auto *SE = dyn_cast<StringLiteral>(Init))
-    if (CheckC23ConstexprInitStringLiteral(SE, S))
-      return true;
-
-  for (const Stmt *SubStmt : Init->children()) {
-    const Expr *ChildExpr = dyn_cast_or_null<Expr>(SubStmt);
-    if (!ChildExpr)
-      continue;
-
-    if (CheckC23ConstexprInitializer(S, ChildExpr))
-      return true;
-  }
-  return false;
-}
-
 void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
   if (var->isInvalidDecl()) return;
 
@@ -14687,8 +14581,6 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
 
     if (HasConstInit) {
       // FIXME: Consider replacing the initializer with a ConstantExpr.
-      if (getLangOpts().C23 && var->isConstexpr())
-        CheckC23ConstexprInitializer(*this, Init);
     } else if (var->isConstexpr()) {
       SourceLocation DiagLoc = var->getLocation();
       // If the note doesn't add any useful information other than a source
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 0fd458837163e5..8bed3a457d84f5 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));
@@ -8366,6 +8392,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) {
@@ -9192,6 +9221,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,
@@ -9209,7 +9255,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;
     }
 
@@ -10498,6 +10546,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/test/C/C2x/n3018.c b/clang/test/C/C2x/n3018.c
index ccc0b708a67e06..f26cb468452e19 100644
--- a/clang/test/C/C2x/n3018.c
+++ b/clang/test/C/C2x/n3018.c
@@ -9,7 +9,7 @@
 
 void Example0() {
   constexpr unsigned int minusOne    = -1;
-  // expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned int'}}
+  // 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);
@@ -19,7 +19,7 @@ void Example0() {
   // 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 'unsigned char'}}
+  // 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
@@ -44,12 +44,12 @@ 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 'short'}}
+  // 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 'float'}}
+  // 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;
 }
 
@@ -58,15 +58,16 @@ 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 'unsigned short'}}
-      -1       // constraint violation, target type is unsigned
+      // 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 'unsigned short'}}
+       // expected-error at -1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned short'}}
   };
 
   struct S {
diff --git a/clang/test/Parser/c23-constexpr.c b/clang/test/Parser/c23-constexpr.c
index 5eddf66a66e74b..1f3470c43386fe 100644
--- a/clang/test/Parser/c23-constexpr.c
+++ b/clang/test/Parser/c23-constexpr.c
@@ -4,3 +4,14 @@
 
 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
index e247773d98d127..c2158914d3af79 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c2x -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion -Wno-div-by-zero %s
+// 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}}
@@ -37,13 +37,16 @@ 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";
+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;
@@ -64,16 +67,21 @@ constexpr volatile int V17 = 0; // expected-error {{constexpr variable cannot ha
 
 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' (aka 'const _Atomic(int)')}}
+// 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' (aka 'const volatile short')}}
+// 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' (aka 'float *const restrict')}}
+// expected-error at -1 {{constexpr variable cannot have type 'const TheR[3]' (aka 'float *restrict const[3]')}}
 
 struct HasA {
   TheA f;
@@ -119,6 +127,15 @@ 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;
@@ -176,7 +193,6 @@ void f4(const int P1) {
   constexpr int V1 = 12;
   constexpr const int *V2 = &V1;
 // expected-error at -1 {{constexpr variable 'V2' must be initialized by a constant expression}}
-// expected-error at -2 {{constexpr pointer initializer is not null}}
 }
 
 // Check that initializer for constexpr variable should match the type of the
@@ -193,16 +209,24 @@ struct S7 {
   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 'char'}}
+  // 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 'float'}}
+  // 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 'float'}}
+  // 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 'float'}}
+  // 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'}}
@@ -220,41 +244,42 @@ void f5() {
   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 'float'}}
+  // 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 'unsigned char'}}
+  // 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 'char'}}
+  // 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 'int' is of type 'double'}}
+  // 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 'int' is of type 'const double'}}
+  // 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 'unsigned int' is of type 'double'}}
+  // 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 'float' is of type 'const _Complex float'}}
+  // 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 'int' is of type 'const _Complex float'}}
+  // expected-error at -1 {{constexpr initializer for type 'const int' is of type 'const _Complex float'}}
 
 }
 
@@ -266,11 +291,15 @@ constexpr char u8string[] = u8"test"u8"ing this out\xFF";
 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 'unsigned int'}}
+// 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;
@@ -281,6 +310,7 @@ 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.
@@ -294,3 +324,9 @@ void f6(const int P1) {
   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]')}}
+}

>From 62ab5efb4ee8417af6d9c2351153a726b7f4486a Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Mon, 26 Feb 2024 09:18:31 -0800
Subject: [PATCH 13/14] Minors to the tests

---
 clang/test/C/C2x/n3018.c          | 2 +-
 clang/test/Parser/c23-constexpr.c | 2 --
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/clang/test/C/C2x/n3018.c b/clang/test/C/C2x/n3018.c
index f26cb468452e19..0d54d53b7499fc 100644
--- a/clang/test/C/C2x/n3018.c
+++ b/clang/test/C/C2x/n3018.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c2x -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion %s
+// 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
diff --git a/clang/test/Parser/c23-constexpr.c b/clang/test/Parser/c23-constexpr.c
index 1f3470c43386fe..156128fa0745cd 100644
--- a/clang/test/Parser/c23-constexpr.c
+++ b/clang/test/Parser/c23-constexpr.c
@@ -1,11 +1,9 @@
 // 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}}
 

>From dff61e0f49d0ba0d868f4e9668f75718b031e1e3 Mon Sep 17 00:00:00 2001
From: "Podchishchaeva, Mariya" <mariya.podchishchaeva at intel.com>
Date: Tue, 5 Mar 2024 03:01:15 -0800
Subject: [PATCH 14/14] Implement NaN cases

---
 clang/lib/Sema/SemaOverload.cpp | 27 +++++++++++++++++++++++++++
 clang/test/Sema/constexpr.c     | 27 +++++++++++++++++++++++++++
 2 files changed, 54 insertions(+)

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 1876323f106b94..abff3034b5282b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -432,6 +432,14 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
         Converted.convert(Ctx.getFloatTypeSemantics(FromType),
                           llvm::APFloat::rmNearestTiesToEven, &ignored);
         if (Ctx.getLangOpts().C23) {
+          if (FloatVal.isNaN() && Converted.isNaN()) {
+            if (!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;
@@ -514,6 +522,25 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
       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/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index c2158914d3af79..8286cd2107d2f2 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -330,3 +330,30 @@ void f7(int n, int array[n]) {
   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;
+}



More information about the cfe-commits mailing list