[clang] [Clang] Add __builtin_is_within_lifetime to implement P2641R4's std::is_within_lifetime (PR #91895)

2024-05-20 Thread Mital Ashok via cfe-commits

https://github.com/MitalAshok ready_for_review 
https://github.com/llvm/llvm-project/pull/91895
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Add __builtin_is_within_lifetime to implement P2641R4's std::is_within_lifetime (PR #91895)

2024-05-20 Thread Mital Ashok via cfe-commits

https://github.com/MitalAshok updated 
https://github.com/llvm/llvm-project/pull/91895

>From 56aed689dc5825fc5bacc6dfdff58ee0eaf71f82 Mon Sep 17 00:00:00 2001
From: Mital Ashok 
Date: Sun, 12 May 2024 19:48:24 +0100
Subject: [PATCH 1/9] [Clang] Add attribute for consteval builtins; Declare
 constexpr builtins as constexpr in C++

Also support redeclaring now-constexpr builtins without constexpr
---
 clang/include/clang/Basic/Builtins.h  |  5 +
 clang/include/clang/Basic/BuiltinsBase.td |  2 ++
 clang/lib/Sema/SemaDecl.cpp   | 15 +++
 clang/lib/Sema/SemaDeclCXX.cpp| 18 +-
 clang/lib/Sema/SemaExpr.cpp   |  8 ++--
 clang/test/Sema/builtin-redecl.cpp| 15 ++-
 6 files changed, 47 insertions(+), 16 deletions(-)

diff --git a/clang/include/clang/Basic/Builtins.h 
b/clang/include/clang/Basic/Builtins.h
index f955d21169556..e85ec5b2dca14 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -280,6 +280,11 @@ class Context {
 return strchr(getRecord(ID).Attributes, 'E') != nullptr;
   }
 
+  /// Returns true if this is an immediate (consteval) function
+  bool isImmediate(unsigned ID) const {
+return strchr(getRecord(ID).Attributes, 'G') != nullptr;
+  }
+
 private:
   const Info (unsigned ID) const;
 
diff --git a/clang/include/clang/Basic/BuiltinsBase.td 
b/clang/include/clang/Basic/BuiltinsBase.td
index 724747ec76d73..1196b9e15c10d 100644
--- a/clang/include/clang/Basic/BuiltinsBase.td
+++ b/clang/include/clang/Basic/BuiltinsBase.td
@@ -70,6 +70,8 @@ class VScanfFormat : IndexedAttribute<"S", I>;
 
 // Builtin can be constant evaluated
 def Constexpr : Attribute<"E">;
+// Builtin is immediate and must be constant evaluated. Implies Constexpr.
+def Consteval : Attribute<"EG">;
 
 // Builtin kinds
 // =
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index fb913034bd836..6b0a04585928a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2409,10 +2409,17 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, 
QualType Type,
 Parent = CLinkageDecl;
   }
 
-  FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type,
-   /*TInfo=*/nullptr, SC_Extern,
-   
getCurFPFeatures().isFPConstrained(),
-   false, Type->isFunctionProtoType());
+  ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified;
+  if (getLangOpts().CPlusPlus && Context.BuiltinInfo.isConstantEvaluated(ID)) {
+ConstexprKind = ConstexprSpecKind::Constexpr;
+if (Context.BuiltinInfo.isImmediate(ID))
+  ConstexprKind = ConstexprSpecKind::Consteval;
+  }
+
+  FunctionDecl *New = FunctionDecl::Create(
+  Context, Parent, Loc, Loc, II, Type, /*TInfo=*/nullptr, SC_Extern,
+  getCurFPFeatures().isFPConstrained(), /*isInlineSpecified=*/false,
+  Type->isFunctionProtoType(), ConstexprKind);
   New->setImplicit();
   New->addAttr(BuiltinAttr::CreateImplicit(Context, ID));
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 53238d355ea09..1b558d70f9b48 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -676,11 +676,19 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
   // template has a constexpr specifier then all its declarations shall
   // contain the constexpr specifier.
   if (New->getConstexprKind() != Old->getConstexprKind()) {
-Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch)
-<< New << static_cast(New->getConstexprKind())
-<< static_cast(Old->getConstexprKind());
-Diag(Old->getLocation(), diag::note_previous_declaration);
-Invalid = true;
+if (Old->getBuiltinID() &&
+Old->getConstexprKind() == ConstexprSpecKind::Constexpr &&
+New->getConstexprKind() == ConstexprSpecKind::Unspecified) {
+  // Except allow redeclaring a builtin as non-constexpr to match C
+  // redeclarations which will not be constexpr
+  New->setConstexprKind(ConstexprSpecKind::Constexpr);
+} else {
+  Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch)
+  << New << static_cast(New->getConstexprKind())
+  << static_cast(Old->getConstexprKind());
+  Diag(Old->getLocation(), diag::note_previous_declaration);
+  Invalid = true;
+}
   } else if (!Old->getMostRecentDecl()->isInlined() && New->isInlined() &&
  Old->isDefined(Def) &&
  // If a friend function is inlined but does not have 'inline'
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index bb4b116fd73ca..39aa32526d2b1 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7095,8 +7095,12 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, 
NamedDecl *NDecl,
   }
 
   // Bail out 

[clang] [Clang] Add __builtin_is_within_lifetime to implement P2641R4's std::is_within_lifetime (PR #91895)

2024-05-20 Thread Mital Ashok via cfe-commits

https://github.com/MitalAshok edited 
https://github.com/llvm/llvm-project/pull/91895
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Add __builtin_is_within_lifetime to implement P2641R4's std::is_within_lifetime (PR #91895)

2024-05-20 Thread Mital Ashok via cfe-commits

https://github.com/MitalAshok updated 
https://github.com/llvm/llvm-project/pull/91895

>From 56aed689dc5825fc5bacc6dfdff58ee0eaf71f82 Mon Sep 17 00:00:00 2001
From: Mital Ashok 
Date: Sun, 12 May 2024 19:48:24 +0100
Subject: [PATCH 1/8] [Clang] Add attribute for consteval builtins; Declare
 constexpr builtins as constexpr in C++

Also support redeclaring now-constexpr builtins without constexpr
---
 clang/include/clang/Basic/Builtins.h  |  5 +
 clang/include/clang/Basic/BuiltinsBase.td |  2 ++
 clang/lib/Sema/SemaDecl.cpp   | 15 +++
 clang/lib/Sema/SemaDeclCXX.cpp| 18 +-
 clang/lib/Sema/SemaExpr.cpp   |  8 ++--
 clang/test/Sema/builtin-redecl.cpp| 15 ++-
 6 files changed, 47 insertions(+), 16 deletions(-)

diff --git a/clang/include/clang/Basic/Builtins.h 
b/clang/include/clang/Basic/Builtins.h
index f955d21169556..e85ec5b2dca14 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -280,6 +280,11 @@ class Context {
 return strchr(getRecord(ID).Attributes, 'E') != nullptr;
   }
 
+  /// Returns true if this is an immediate (consteval) function
+  bool isImmediate(unsigned ID) const {
+return strchr(getRecord(ID).Attributes, 'G') != nullptr;
+  }
+
 private:
   const Info (unsigned ID) const;
 
diff --git a/clang/include/clang/Basic/BuiltinsBase.td 
b/clang/include/clang/Basic/BuiltinsBase.td
index 724747ec76d73..1196b9e15c10d 100644
--- a/clang/include/clang/Basic/BuiltinsBase.td
+++ b/clang/include/clang/Basic/BuiltinsBase.td
@@ -70,6 +70,8 @@ class VScanfFormat : IndexedAttribute<"S", I>;
 
 // Builtin can be constant evaluated
 def Constexpr : Attribute<"E">;
+// Builtin is immediate and must be constant evaluated. Implies Constexpr.
+def Consteval : Attribute<"EG">;
 
 // Builtin kinds
 // =
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index fb913034bd836..6b0a04585928a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2409,10 +2409,17 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, 
QualType Type,
 Parent = CLinkageDecl;
   }
 
-  FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type,
-   /*TInfo=*/nullptr, SC_Extern,
-   
getCurFPFeatures().isFPConstrained(),
-   false, Type->isFunctionProtoType());
+  ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified;
+  if (getLangOpts().CPlusPlus && Context.BuiltinInfo.isConstantEvaluated(ID)) {
+ConstexprKind = ConstexprSpecKind::Constexpr;
+if (Context.BuiltinInfo.isImmediate(ID))
+  ConstexprKind = ConstexprSpecKind::Consteval;
+  }
+
+  FunctionDecl *New = FunctionDecl::Create(
+  Context, Parent, Loc, Loc, II, Type, /*TInfo=*/nullptr, SC_Extern,
+  getCurFPFeatures().isFPConstrained(), /*isInlineSpecified=*/false,
+  Type->isFunctionProtoType(), ConstexprKind);
   New->setImplicit();
   New->addAttr(BuiltinAttr::CreateImplicit(Context, ID));
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 53238d355ea09..1b558d70f9b48 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -676,11 +676,19 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
   // template has a constexpr specifier then all its declarations shall
   // contain the constexpr specifier.
   if (New->getConstexprKind() != Old->getConstexprKind()) {
-Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch)
-<< New << static_cast(New->getConstexprKind())
-<< static_cast(Old->getConstexprKind());
-Diag(Old->getLocation(), diag::note_previous_declaration);
-Invalid = true;
+if (Old->getBuiltinID() &&
+Old->getConstexprKind() == ConstexprSpecKind::Constexpr &&
+New->getConstexprKind() == ConstexprSpecKind::Unspecified) {
+  // Except allow redeclaring a builtin as non-constexpr to match C
+  // redeclarations which will not be constexpr
+  New->setConstexprKind(ConstexprSpecKind::Constexpr);
+} else {
+  Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch)
+  << New << static_cast(New->getConstexprKind())
+  << static_cast(Old->getConstexprKind());
+  Diag(Old->getLocation(), diag::note_previous_declaration);
+  Invalid = true;
+}
   } else if (!Old->getMostRecentDecl()->isInlined() && New->isInlined() &&
  Old->isDefined(Def) &&
  // If a friend function is inlined but does not have 'inline'
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index bb4b116fd73ca..39aa32526d2b1 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7095,8 +7095,12 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, 
NamedDecl *NDecl,
   }
 
   // Bail out 

[clang] [Clang] Add __builtin_is_within_lifetime to implement P2641R4's std::is_within_lifetime (PR #91895)

2024-05-12 Thread Mital Ashok via cfe-commits

MitalAshok wrote:

This is on top of #91894

[P2641R4](https://wg21.link/P2641R4)

Currently, this doesn't strictly check "whose complete object's lifetime began 
within `E`". A bunch of the static_asserts I have for objects under 
construction should be ill-formed instead of false, but some of them should be 
true.

https://github.com/llvm/llvm-project/pull/91895
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Add __builtin_is_within_lifetime to implement P2641R4's std::is_within_lifetime (PR #91895)

2024-05-12 Thread via cfe-commits

github-actions[bot] wrote:




:warning: C/C++ code formatter, clang-format found issues in your code. 
:warning:



You can test this locally with the following command:


``bash
git-clang-format --diff 17daa204feadf9c28fc13b7daa69c3cbe865b238 
253f58f0e34bc6dedbbfe17f68cfe9baa3a9146f -- 
clang/test/SemaCXX/builtin-is-within-lifetime.cpp 
clang/include/clang/Basic/Builtins.h clang/lib/AST/ExprConstant.cpp 
clang/lib/AST/Interp/State.h clang/lib/Sema/SemaChecking.cpp 
clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclCXX.cpp 
clang/lib/Sema/SemaExpr.cpp clang/test/Sema/builtin-redecl.cpp
``





View the diff from clang-format here.


``diff
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f3b31d87dd..ffe2f172a9 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1507,7 +1507,8 @@ CallStackFrame::~CallStackFrame() {
 }
 
 static bool isRead(AccessKinds AK) {
-  return AK == AK_Read || AK == AK_ReadObjectRepresentation || AK == 
AK_IsWithinLifetime;
+  return AK == AK_Read || AK == AK_ReadObjectRepresentation ||
+ AK == AK_IsWithinLifetime;
 }
 
 static bool isModification(AccessKinds AK) {
@@ -1535,7 +1536,8 @@ static bool isAnyAccess(AccessKinds AK) {
 
 /// Is this an access per the C++ definition?
 static bool isFormalAccess(AccessKinds AK) {
-  return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy && AK != 
AK_IsWithinLifetime;
+  return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy &&
+ AK != AK_IsWithinLifetime;
 }
 
 /// Is this kind of axcess valid on an indeterminate object value?
@@ -3655,7 +3657,8 @@ struct CompleteObject {
 // In C++14 onwards, it is permitted to read a mutable member whose
 // lifetime began within the evaluation.
 // FIXME: Should we also allow this in C++11?
-if (!Info.getLangOpts().CPlusPlus14 && AK != 
AccessKinds::AK_IsWithinLifetime)
+if (!Info.getLangOpts().CPlusPlus14 &&
+AK != AccessKinds::AK_IsWithinLifetime)
   return false;
 return lifetimeStartedInEvaluation(Info, Base, /*MutableSubobject*/true);
   }
@@ -4093,7 +4096,7 @@ static CompleteObject findCompleteObject(EvalInfo , 
const Expr *E,
 // started in the current evaluation.
 BaseVal = Info.EvaluatingDeclValue;
 if (AK == AccessKinds::AK_IsWithinLifetime)
-  return CompleteObject();  // Not within lifetime
+  return CompleteObject(); // Not within lifetime
   } else if (const ValueDecl *D = LVal.Base.dyn_cast()) {
 // Allow reading from a GUID declaration.
 if (auto *GD = dyn_cast(D)) {
@@ -11508,7 +11511,8 @@ public:
 
   bool ZeroInitialization(const Expr *E) { return Success(0, E); }
 
-  friend std::optional EvaluateBuiltinIsWithinLifetime(IntExprEvaluator 
&, const CallExpr *);
+  friend std::optional EvaluateBuiltinIsWithinLifetime(IntExprEvaluator 
&,
+ const CallExpr *);
 
   
//======//
   //Visitor Methods
@@ -17030,56 +17034,70 @@ bool Expr::tryEvaluateStrLen(uint64_t , 
ASTContext ) const {
 }
 
 namespace {
-  struct IsWithinLifetimeHandler {
-EvalInfo 
-static constexpr AccessKinds AccessKind = AccessKinds::AK_IsWithinLifetime;
-using result_type = std::optional;
-std::optional failed() { return std::nullopt; }
-template
-std::optional found(T , QualType SubobjType) {
-  return true;
-}
-  };
+struct IsWithinLifetimeHandler {
+  EvalInfo 
+  static constexpr AccessKinds AccessKind = AccessKinds::AK_IsWithinLifetime;
+  using result_type = std::optional;
+  std::optional failed() { return std::nullopt; }
+  template 
+  std::optional found(T , QualType SubobjType) {
+return true;
+  }
+};
 
-  std::optional EvaluateBuiltinIsWithinLifetime(IntExprEvaluator , 
const CallExpr *E) {
-EvalInfo& Info = IEE.Info;
-//assert(Info.InConstantContext && "Call to consteval builtin not in 
constant context?");
-assert(E->getBuiltinCallee() == Builtin::BI__builtin_is_within_lifetime);
-const Expr *Arg = E->getArg(0);
-if (Arg->isValueDependent())
-  return std::nullopt;
-LValue Val;
-if (!EvaluatePointer(Arg, Val, Info))
-  return std::nullopt;
+std::optional EvaluateBuiltinIsWithinLifetime(IntExprEvaluator ,
+const CallExpr *E) {
+  EvalInfo  = IEE.Info;
+  // assert(Info.InConstantContext && "Call to consteval builtin not in 
constant
+  // context?");
+  assert(E->getBuiltinCallee() == Builtin::BI__builtin_is_within_lifetime);
+  const Expr *Arg = E->getArg(0);
+  if (Arg->isValueDependent())
+return std::nullopt;
+  LValue Val;
+  if (!EvaluatePointer(Arg, Val, Info))
+return std::nullopt;
 
-auto Error = [&](int Diag) {
-  const auto *Callee = Info.CurrentCall->getCallee();
-  bool CalledFromStd = Callee && 

[clang] [Clang] Add __builtin_is_within_lifetime to implement P2641R4's std::is_within_lifetime (PR #91895)

2024-05-12 Thread Mital Ashok via cfe-commits

https://github.com/MitalAshok created 
https://github.com/llvm/llvm-project/pull/91895

None

>From 56aed689dc5825fc5bacc6dfdff58ee0eaf71f82 Mon Sep 17 00:00:00 2001
From: Mital Ashok 
Date: Sun, 12 May 2024 19:48:24 +0100
Subject: [PATCH 1/2] [Clang] Add attribute for consteval builtins; Declare
 constexpr builtins as constexpr in C++

Also support redeclaring now-constexpr builtins without constexpr
---
 clang/include/clang/Basic/Builtins.h  |  5 +
 clang/include/clang/Basic/BuiltinsBase.td |  2 ++
 clang/lib/Sema/SemaDecl.cpp   | 15 +++
 clang/lib/Sema/SemaDeclCXX.cpp| 18 +-
 clang/lib/Sema/SemaExpr.cpp   |  8 ++--
 clang/test/Sema/builtin-redecl.cpp| 15 ++-
 6 files changed, 47 insertions(+), 16 deletions(-)

diff --git a/clang/include/clang/Basic/Builtins.h 
b/clang/include/clang/Basic/Builtins.h
index f955d21169556..e85ec5b2dca14 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -280,6 +280,11 @@ class Context {
 return strchr(getRecord(ID).Attributes, 'E') != nullptr;
   }
 
+  /// Returns true if this is an immediate (consteval) function
+  bool isImmediate(unsigned ID) const {
+return strchr(getRecord(ID).Attributes, 'G') != nullptr;
+  }
+
 private:
   const Info (unsigned ID) const;
 
diff --git a/clang/include/clang/Basic/BuiltinsBase.td 
b/clang/include/clang/Basic/BuiltinsBase.td
index 724747ec76d73..1196b9e15c10d 100644
--- a/clang/include/clang/Basic/BuiltinsBase.td
+++ b/clang/include/clang/Basic/BuiltinsBase.td
@@ -70,6 +70,8 @@ class VScanfFormat : IndexedAttribute<"S", I>;
 
 // Builtin can be constant evaluated
 def Constexpr : Attribute<"E">;
+// Builtin is immediate and must be constant evaluated. Implies Constexpr.
+def Consteval : Attribute<"EG">;
 
 // Builtin kinds
 // =
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index fb913034bd836..6b0a04585928a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2409,10 +2409,17 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, 
QualType Type,
 Parent = CLinkageDecl;
   }
 
-  FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type,
-   /*TInfo=*/nullptr, SC_Extern,
-   
getCurFPFeatures().isFPConstrained(),
-   false, Type->isFunctionProtoType());
+  ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified;
+  if (getLangOpts().CPlusPlus && Context.BuiltinInfo.isConstantEvaluated(ID)) {
+ConstexprKind = ConstexprSpecKind::Constexpr;
+if (Context.BuiltinInfo.isImmediate(ID))
+  ConstexprKind = ConstexprSpecKind::Consteval;
+  }
+
+  FunctionDecl *New = FunctionDecl::Create(
+  Context, Parent, Loc, Loc, II, Type, /*TInfo=*/nullptr, SC_Extern,
+  getCurFPFeatures().isFPConstrained(), /*isInlineSpecified=*/false,
+  Type->isFunctionProtoType(), ConstexprKind);
   New->setImplicit();
   New->addAttr(BuiltinAttr::CreateImplicit(Context, ID));
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 53238d355ea09..1b558d70f9b48 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -676,11 +676,19 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
   // template has a constexpr specifier then all its declarations shall
   // contain the constexpr specifier.
   if (New->getConstexprKind() != Old->getConstexprKind()) {
-Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch)
-<< New << static_cast(New->getConstexprKind())
-<< static_cast(Old->getConstexprKind());
-Diag(Old->getLocation(), diag::note_previous_declaration);
-Invalid = true;
+if (Old->getBuiltinID() &&
+Old->getConstexprKind() == ConstexprSpecKind::Constexpr &&
+New->getConstexprKind() == ConstexprSpecKind::Unspecified) {
+  // Except allow redeclaring a builtin as non-constexpr to match C
+  // redeclarations which will not be constexpr
+  New->setConstexprKind(ConstexprSpecKind::Constexpr);
+} else {
+  Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch)
+  << New << static_cast(New->getConstexprKind())
+  << static_cast(Old->getConstexprKind());
+  Diag(Old->getLocation(), diag::note_previous_declaration);
+  Invalid = true;
+}
   } else if (!Old->getMostRecentDecl()->isInlined() && New->isInlined() &&
  Old->isDefined(Def) &&
  // If a friend function is inlined but does not have 'inline'
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index bb4b116fd73ca..39aa32526d2b1 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7095,8 +7095,12 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, 
NamedDecl *NDecl,
   }
 
   // Bail