https://github.com/shafik updated 
https://github.com/llvm/llvm-project/pull/95474

>From 69b09ea5b0f0a1c5419c488ade29b6fedc6de773 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Thu, 13 Jun 2024 14:20:50 -0700
Subject: [PATCH 01/10] [Clang] Implement P2280R4 Using unknown pointers and
 references in constant expressions

P2280R4 allows the use of references in pointers of unknown origins in a
constant expression context but only in specific cases that could be constant
expressions.

We track whether a variable is a constexpr unknown in a constant expression by
setting a flag in either APValue or LValue and using this flag to prevent using
unknown values in places where it is not allowed.

In `evaluateVarDeclInit` we may need to create a new `APValue` to track the
unknown referene or pointer and we track that `APValue` in the
`CallStackFrame`.

Fixes: https://github.com/llvm/llvm-project/issues/63139
https://github.com/llvm/llvm-project/issues/63117
---
 clang/include/clang/AST/APValue.h             | 48 +++++++----
 clang/lib/AST/APValue.cpp                     | 12 ++-
 clang/lib/AST/ExprConstant.cpp                | 85 +++++++++++++++++--
 .../SemaCXX/constant-expression-cxx11.cpp     | 16 ++--
 .../SemaCXX/constant-expression-cxx2a.cpp     |  3 +-
 .../SemaCXX/constant-expression-p2280r4.cpp   | 54 ++++++++++++
 6 files changed, 183 insertions(+), 35 deletions(-)
 create mode 100644 clang/test/SemaCXX/constant-expression-p2280r4.cpp

diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index c4206b73b11562..6352348107a647 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -249,6 +249,7 @@ class APValue {
   struct NoLValuePath {};
   struct UninitArray {};
   struct UninitStruct {};
+  struct ConstexprUnknown {};
 
   template <typename Impl> friend class clang::serialization::BasicReaderBase;
   friend class ASTImporter;
@@ -256,6 +257,7 @@ class APValue {
 
 private:
   ValueKind Kind;
+  bool AllowConstexprUnknown = false;
 
   struct ComplexAPSInt {
     APSInt Real, Imag;
@@ -314,53 +316,69 @@ class APValue {
   DataType Data;
 
 public:
-  APValue() : Kind(None) {}
-  explicit APValue(APSInt I) : Kind(None) {
+  bool allowConstexprUnknown() const { return AllowConstexprUnknown; }
+
+  void setConstexprUnknown() { AllowConstexprUnknown = true; }
+
+  APValue() : Kind(None), AllowConstexprUnknown(false) {}
+  explicit APValue(APSInt I) : Kind(None), AllowConstexprUnknown(false) {
     MakeInt(); setInt(std::move(I));
   }
-  explicit APValue(APFloat F) : Kind(None) {
+  explicit APValue(APFloat F) : Kind(None), AllowConstexprUnknown(false) {
     MakeFloat(); setFloat(std::move(F));
   }
-  explicit APValue(APFixedPoint FX) : Kind(None) {
+  explicit APValue(APFixedPoint FX) : Kind(None), AllowConstexprUnknown(false) 
{
     MakeFixedPoint(std::move(FX));
   }
-  explicit APValue(const APValue *E, unsigned N) : Kind(None) {
+  explicit APValue(const APValue *E, unsigned N)
+      : Kind(None), AllowConstexprUnknown(false) {
     MakeVector(); setVector(E, N);
   }
-  APValue(APSInt R, APSInt I) : Kind(None) {
+  APValue(APSInt R, APSInt I) : Kind(None), AllowConstexprUnknown(false) {
     MakeComplexInt(); setComplexInt(std::move(R), std::move(I));
   }
-  APValue(APFloat R, APFloat I) : Kind(None) {
+  APValue(APFloat R, APFloat I) : Kind(None), AllowConstexprUnknown(false) {
     MakeComplexFloat(); setComplexFloat(std::move(R), std::move(I));
   }
   APValue(const APValue &RHS);
   APValue(APValue &&RHS);
   APValue(LValueBase B, const CharUnits &O, NoLValuePath N,
           bool IsNullPtr = false)
-      : Kind(None) {
+      : Kind(None), AllowConstexprUnknown(false) {
     MakeLValue(); setLValue(B, O, N, IsNullPtr);
   }
   APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path,
           bool OnePastTheEnd, bool IsNullPtr = false)
-      : Kind(None) {
+      : Kind(None), AllowConstexprUnknown(false) {
     MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, IsNullPtr);
   }
-  APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(None) {
+
+  APValue(LValueBase B, ConstexprUnknown, const CharUnits &O,
+          bool IsNullPtr = false)
+      : Kind(None), AllowConstexprUnknown(true) {
+    MakeLValue();
+    setLValue(B, O, NoLValuePath{}, IsNullPtr);
+  }
+
+  APValue(UninitArray, unsigned InitElts, unsigned Size)
+      : Kind(None), AllowConstexprUnknown(false) {
     MakeArray(InitElts, Size);
   }
-  APValue(UninitStruct, unsigned B, unsigned M) : Kind(None) {
+  APValue(UninitStruct, unsigned B, unsigned M)
+      : Kind(None), AllowConstexprUnknown(false) {
     MakeStruct(B, M);
   }
   explicit APValue(const FieldDecl *D, const APValue &V = APValue())
-      : Kind(None) {
+      : Kind(None), AllowConstexprUnknown(false) {
     MakeUnion(); setUnion(D, V);
   }
   APValue(const ValueDecl *Member, bool IsDerivedMember,
-          ArrayRef<const CXXRecordDecl*> Path) : Kind(None) {
+          ArrayRef<const CXXRecordDecl *> Path)
+      : Kind(None), AllowConstexprUnknown(false) {
     MakeMemberPointer(Member, IsDerivedMember, Path);
   }
-  APValue(const AddrLabelExpr* LHSExpr, const AddrLabelExpr* RHSExpr)
-      : Kind(None) {
+  APValue(const AddrLabelExpr *LHSExpr, const AddrLabelExpr *RHSExpr)
+      : Kind(None), AllowConstexprUnknown(false) {
     MakeAddrLabelDiff(); setAddrLabelDiff(LHSExpr, RHSExpr);
   }
   static APValue IndeterminateValue() {
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index d8e33ff421c06c..f7841cb6556de7 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -308,7 +308,8 @@ APValue::UnionData::~UnionData () {
   delete Value;
 }
 
-APValue::APValue(const APValue &RHS) : Kind(None) {
+APValue::APValue(const APValue &RHS)
+    : Kind(None), AllowConstexprUnknown(RHS.AllowConstexprUnknown) {
   switch (RHS.getKind()) {
   case None:
   case Indeterminate:
@@ -379,13 +380,17 @@ APValue::APValue(const APValue &RHS) : Kind(None) {
   }
 }
 
-APValue::APValue(APValue &&RHS) : Kind(RHS.Kind), Data(RHS.Data) {
+APValue::APValue(APValue &&RHS)
+    : Kind(RHS.Kind), AllowConstexprUnknown(RHS.AllowConstexprUnknown),
+      Data(RHS.Data) {
   RHS.Kind = None;
 }
 
 APValue &APValue::operator=(const APValue &RHS) {
   if (this != &RHS)
     *this = APValue(RHS);
+
+  AllowConstexprUnknown = RHS.AllowConstexprUnknown;
   return *this;
 }
 
@@ -395,6 +400,7 @@ APValue &APValue::operator=(APValue &&RHS) {
       DestroyDataAndMakeUninit();
     Kind = RHS.Kind;
     Data = RHS.Data;
+    AllowConstexprUnknown = RHS.AllowConstexprUnknown;
     RHS.Kind = None;
   }
   return *this;
@@ -426,6 +432,7 @@ void APValue::DestroyDataAndMakeUninit() {
   else if (Kind == AddrLabelDiff)
     ((AddrLabelDiffData *)(char *)&Data)->~AddrLabelDiffData();
   Kind = None;
+  AllowConstexprUnknown = false;
 }
 
 bool APValue::needsCleanup() const {
@@ -468,6 +475,7 @@ bool APValue::needsCleanup() const {
 void APValue::swap(APValue &RHS) {
   std::swap(Kind, RHS.Kind);
   std::swap(Data, RHS.Data);
+  std::swap(AllowConstexprUnknown, RHS.AllowConstexprUnknown);
 }
 
 /// Profile the value of an APInt, excluding its bit-width.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 7178f081d9cf35..9a427d908e4c91 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1609,8 +1609,11 @@ namespace {
     SubobjectDesignator Designator;
     bool IsNullPtr : 1;
     bool InvalidBase : 1;
+    // P2280R4 track if we have an unknown reference or pointer.
+    bool AllowConstexprUnknown = false;
 
     const APValue::LValueBase getLValueBase() const { return Base; }
+    bool allowConstexprUnknown() const { return AllowConstexprUnknown; }
     CharUnits &getLValueOffset() { return Offset; }
     const CharUnits &getLValueOffset() const { return Offset; }
     SubobjectDesignator &getLValueDesignator() { return Designator; }
@@ -1628,6 +1631,8 @@ namespace {
         V = APValue(Base, Offset, Designator.Entries,
                     Designator.IsOnePastTheEnd, IsNullPtr);
       }
+      if (AllowConstexprUnknown)
+        V.setConstexprUnknown();
     }
     void setFrom(ASTContext &Ctx, const APValue &V) {
       assert(V.isLValue() && "Setting LValue from a non-LValue?");
@@ -1636,6 +1641,7 @@ namespace {
       InvalidBase = false;
       Designator = SubobjectDesignator(Ctx, V);
       IsNullPtr = V.isNullPointer();
+      AllowConstexprUnknown = V.allowConstexprUnknown();
     }
 
     void set(APValue::LValueBase B, bool BInvalid = false) {
@@ -1653,6 +1659,7 @@ namespace {
       InvalidBase = BInvalid;
       Designator = SubobjectDesignator(getType(B));
       IsNullPtr = false;
+      AllowConstexprUnknown = false;
     }
 
     void setNull(ASTContext &Ctx, QualType PointerTy) {
@@ -1662,6 +1669,7 @@ namespace {
       InvalidBase = false;
       Designator = SubobjectDesignator(PointerTy->getPointeeType());
       IsNullPtr = true;
+      AllowConstexprUnknown = false;
     }
 
     void setInvalid(APValue::LValueBase B, unsigned I = 0) {
@@ -3300,6 +3308,11 @@ static bool HandleLValueComplexElement(EvalInfo &Info, 
const Expr *E,
 static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
                                 const VarDecl *VD, CallStackFrame *Frame,
                                 unsigned Version, APValue *&Result) {
+  // P2280R4 If we have a reference type and we are in C++23 allow unknown
+  // references and pointers.
+  bool AllowConstexprUnknown =
+      Info.getLangOpts().CPlusPlus23 && VD->getType()->isReferenceType();
+
   APValue::LValueBase Base(VD, Frame ? Frame->Index : 0, Version);
 
   // If this is a local variable, dig out its value.
@@ -3334,7 +3347,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
     return true;
   }
 
-  if (isa<ParmVarDecl>(VD)) {
+  // P2280R4 struck the restriction that variable of referene type lifetime
+  // should begin within the evaluation of E
+  if (isa<ParmVarDecl>(VD) && !AllowConstexprUnknown) {
     // Assume parameters of a potential constant expression are usable in
     // constant expressions.
     if (!Info.checkingPotentialConstantExpression() ||
@@ -3358,7 +3373,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
   // FIXME: We should eventually check whether the variable has a reachable
   // initializing declaration.
   const Expr *Init = VD->getAnyInitializer(VD);
-  if (!Init) {
+  // P2280R4 struck the restriction that variable of referene type should have
+  // a preceding initialization.
+  if (!Init && !AllowConstexprUnknown) {
     // Don't diagnose during potential constant expression checking; an
     // initializer might be added later.
     if (!Info.checkingPotentialConstantExpression()) {
@@ -3369,7 +3386,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
     return false;
   }
 
-  if (Init->isValueDependent()) {
+  // P2280R4 struck the initialization requirement for variables of reference
+  // type so we can no longer assume we have an Init.
+  if (Init && Init->isValueDependent()) {
     // The DeclRefExpr is not value-dependent, but the variable it refers to
     // has a value-dependent initializer. This should only happen in
     // constant-folding cases, where the variable is not actually of a suitable
@@ -3388,7 +3407,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
 
   // Check that we can fold the initializer. In C++, we will have already done
   // this in the cases where it matters for conformance.
-  if (!VD->evaluateValue()) {
+  // P2280R4 struck the initialization requirement for variables of reference
+  // type so we can no longer assume we have an Init.
+  if (Init && !VD->evaluateValue()) {
     Info.FFDiag(E, diag::note_constexpr_var_init_non_constant, 1) << VD;
     NoteLValueLocation(Info, Base);
     return false;
@@ -3420,6 +3441,15 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
   }
 
   Result = VD->getEvaluatedValue();
+
+  // P2280R4 If we don't have a value because this is a reference that was not
+  // initialized or whose lifetime began within E then create a value with as
+  // a ConstexprUnknown status.
+  if (AllowConstexprUnknown) {
+    if (!Result) {
+      Result = new APValue(Base, APValue::ConstexprUnknown{}, 
CharUnits::One());
+    }
+  }
   return true;
 }
 
@@ -3700,6 +3730,11 @@ findSubobject(EvalInfo &Info, const Expr *E, const 
CompleteObject &Obj,
   const FieldDecl *LastField = nullptr;
   const FieldDecl *VolatileField = nullptr;
 
+  // P2280R4 If we have an unknown referene or pointer and we don't have a
+  // value then bail out.
+  if (O->allowConstexprUnknown() && !O->hasValue())
+    return false;
+
   // Walk the designator's path to find the subobject.
   for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
     // Reading an indeterminate value is undefined, but assigning over one is 
OK.
@@ -5732,6 +5767,12 @@ struct CheckDynamicTypeHandler {
 /// dynamic type.
 static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This,
                              AccessKinds AK, bool Polymorphic) {
+  // P2280R4 We are not allowed to invoke a virtual function whose dynamic type
+  // us constexpr-unknown, so stop early and let this fail later on if we
+  // attempt to do so.
+  if (This.allowConstexprUnknown())
+    return true;
+
   if (This.Designator.Invalid)
     return false;
 
@@ -5804,7 +5845,13 @@ static std::optional<DynamicType> 
ComputeDynamicType(EvalInfo &Info,
   // If we don't have an lvalue denoting an object of class type, there is no
   // meaningful dynamic type. (We consider objects of non-class type to have no
   // dynamic type.)
-  if (!checkDynamicType(Info, E, This, AK, true))
+  if (!checkDynamicType(Info, E, This, AK,
+                        (AK == AK_TypeId
+                             ? (E->getType()->isReferenceType() ? true : false)
+                             : true)))
+    return std::nullopt;
+
+  if (This.Designator.Invalid)
     return std::nullopt;
 
   // Refuse to compute a dynamic type in the presence of virtual bases. This
@@ -8539,7 +8586,8 @@ static bool HandleLambdaCapture(EvalInfo &Info, const 
Expr *E, LValue &Result,
     const ParmVarDecl *Self = MD->getParamDecl(0);
     if (Self->getType()->isReferenceType()) {
       APValue *RefValue = Info.getParamSlot(Info.CurrentCall->Arguments, Self);
-      Result.setFrom(Info.Ctx, *RefValue);
+      if (!RefValue->allowConstexprUnknown() || RefValue->hasValue())
+        Result.setFrom(Info.Ctx, *RefValue);
     } else {
       const ParmVarDecl *VD = Info.CurrentCall->Arguments.getOrigParam(Self);
       CallStackFrame *Frame =
@@ -8595,7 +8643,10 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const 
DeclRefExpr *E) {
 
 
 bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
-
+  // P2280R4 if we are in C++23 track if we have an unknown reference or
+  // pointer.
+  bool AllowConstexprUnknown =
+      Info.getLangOpts().CPlusPlus23 && VD->getType()->isReferenceType();
   // If we are within a lambda's call operator, check whether the 'VD' referred
   // to within 'E' actually represents a lambda-capture that maps to a
   // data-member/field within the closure object, and if so, evaluate to the
@@ -8665,10 +8716,23 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, 
const VarDecl *VD) {
   if (!V->hasValue()) {
     // FIXME: Is it possible for V to be indeterminate here? If so, we should
     // adjust the diagnostic to say that.
-    if (!Info.checkingPotentialConstantExpression())
+    // P2280R4 If we are have a variable that is unknown reference or pointer
+    // it may not have a value but still be usable later on so do not diagnose.
+    if (!Info.checkingPotentialConstantExpression() && !AllowConstexprUnknown)
       Info.FFDiag(E, diag::note_constexpr_use_uninit_reference);
+
+    // P2280R4 If we are have a variable that is unknown reference or pointer
+    // try to recover it from the frame and set the result accordingly.
+    if (VD->getType()->isReferenceType() && AllowConstexprUnknown) {
+      if (Frame) {
+        Result.set({VD, Frame->Index, Version});
+        return true;
+      }
+      return Success(VD);
+    }
     return false;
   }
+
   return Success(*V, E);
 }
 
@@ -11486,7 +11550,10 @@ class IntExprEvaluator
   }
 
   bool Success(const APValue &V, const Expr *E) {
-    if (V.isLValue() || V.isAddrLabelDiff() || V.isIndeterminate()) {
+    // P2280R4 if we have an unknown reference or pointer allow further
+    // evaluation of the value.
+    if (V.isLValue() || V.isAddrLabelDiff() || V.isIndeterminate() ||
+        V.allowConstexprUnknown()) {
       Result = V;
       return true;
     }
diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp 
b/clang/test/SemaCXX/constant-expression-cxx11.cpp
index efb391ba0922d8..684a6a242bf714 100644
--- a/clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp
@@ -1419,9 +1419,9 @@ namespace ConvertedConstantExpr {
   // useless note and instead just point to the non-constant subexpression.
   enum class E {
     em = m,
-    en = n, // expected-error {{not a constant expression}} expected-note 
{{initializer of 'n' is unknown}}
+    en = n, // cxx23-note {{initializer of 'n' is not a constant expression}} 
expected-error {{enumerator value is not a constant expression}} cxx11_20-note 
{{initializer of 'n' is unknown}}
     eo = (m + // expected-error {{not a constant expression}}
-          n // expected-note {{initializer of 'n' is unknown}}
+          n // cxx23-note {{initializer of 'n' is not a constant expression}} 
cxx11_20-note {{initializer of 'n' is unknown}}
           ),
     eq = reinterpret_cast<long>((int*)0) // expected-error {{not a constant 
expression}} expected-note {{reinterpret_cast}}
   };
@@ -1961,7 +1961,8 @@ namespace ConstexprConstructorRecovery {
 
 namespace Lifetime {
   void f() {
-    constexpr int &n = n; // expected-error {{constant expression}} 
expected-note {{use of reference outside its lifetime}} expected-warning {{not 
yet bound to a value}}
+    constexpr int &n = n; // expected-error {{constant expression}} cxx23-note 
{{reference to 'n' is not a constant expression}} cxx23-note {{address of 
non-static constexpr variable 'n' may differ}} expected-warning {{not yet bound 
to a value}}
+                          // cxx11_20-note@-1 {{use of reference outside its 
lifetime is not allowed in a constant expression}}
     constexpr int m = m; // expected-error {{constant expression}} 
expected-note {{read of object outside its lifetime}}
   }
 
@@ -2381,15 +2382,15 @@ namespace array_size {
   template<typename T> void f1(T t) {
     constexpr int k = t.size();
   }
-  template<typename T> void f2(const T &t) { // expected-note 2{{declared 
here}}
-    constexpr int k = t.size(); // expected-error 2{{constant}} expected-note 
2{{function parameter 't' with unknown value cannot be used in a constant 
expression}}
+  template<typename T> void f2(const T &t) { // cxx11_20-note 2{{declared 
here}}
+    constexpr int k = t.size();  // cxx11_20-error 2{{constexpr variable 'k' 
must be initialized by a constant expression}} cxx11_20-note 2{{function 
parameter 't' with unknown value cannot be used in a constant expression}}
   }
   template<typename T> void f3(const T &t) {
     constexpr int k = T::size();
   }
   void g(array<3> a) {
     f1(a);
-    f2(a); // expected-note {{instantiation of}}
+    f2(a); // cxx11_20-note {{in instantiation of function template}}
     f3(a);
   }
 
@@ -2398,8 +2399,9 @@ namespace array_size {
   };
   void h(array_nonstatic<3> a) {
     f1(a);
-    f2(a); // expected-note {{instantiation of}}
+    f2(a); // cxx11_20-note {{instantiation of}}
   }
+  //static_assert(f2(array_size::array<3>{}));
 }
 
 namespace flexible_array {
diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp 
b/clang/test/SemaCXX/constant-expression-cxx2a.cpp
index e4d97dcb73562d..3910886a36346a 100644
--- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp
@@ -308,8 +308,7 @@ namespace TypeId {
   static_assert(&B2().ti1 == &typeid(B));
   static_assert(&B2().ti2 == &typeid(B2));
   extern B2 extern_b2;
-  // expected-note@+1 {{typeid applied to object 'extern_b2' whose dynamic 
type is not constant}}
-  static_assert(&typeid(extern_b2) == &typeid(B2)); // expected-error 
{{constant expression}}
+  static_assert(&typeid(extern_b2) == &typeid(B2));
 
   constexpr B2 b2;
   constexpr const B &b1 = b2;
diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp 
b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
new file mode 100644
index 00000000000000..be41eed88c4944
--- /dev/null
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -std=c++23 -verify %s
+
+using size_t = decltype(sizeof(0));
+
+namespace std {
+struct type_info {
+  const char* name() const noexcept(true);
+};
+}
+
+template <typename T, size_t N>
+constexpr size_t array_size(T (&)[N]) {
+  return N;
+}
+
+void use_array(int const (&gold_medal_mel)[2]) {
+  constexpr auto gold = array_size(gold_medal_mel); // ok
+}
+
+constexpr auto olympic_mile() {
+  const int ledecky = 1500;
+  return []{ return ledecky; };
+}
+static_assert(olympic_mile()() == 1500); // ok
+
+struct Swim {
+  constexpr int phelps() { return 28; }
+  virtual constexpr int lochte() { return 12; }
+  int coughlin = 12;
+};
+
+constexpr int how_many(Swim& swam) {
+  Swim* p = &swam;
+  return (p + 1 - 1)->phelps();
+}
+
+void splash(Swim& swam) {
+  static_assert(swam.phelps() == 28);     // ok
+  static_assert((&swam)->phelps() == 28); // ok
+  Swim* pswam = &swam;                    // expected-note {{declared here}}
+  static_assert(pswam->phelps() == 28);   // expected-error {{static assertion 
expression is not an integral constant expression}}
+                                          // expected-note@-1 {{read of 
non-constexpr variable 'pswam' is not allowed in a constant expression}}
+  static_assert(how_many(swam) == 28);    // ok
+  static_assert(Swim().lochte() == 12);   // ok
+  static_assert(swam.lochte() == 12);     // expected-error {{static assertion 
expression is not an integral constant expression}}
+  static_assert(swam.coughlin == 12);     // expected-error {{static assertion 
expression is not an integral constant expression}}
+}
+
+extern Swim dc;
+extern Swim& trident; // expected-note {{declared here}}
+
+constexpr auto& sandeno   = typeid(dc);         // ok: can only be typeid(Swim)
+constexpr auto& gallagher = typeid(trident);    // expected-error {{constexpr 
variable 'gallagher' must be initialized by a constant expression}}
+                                                // expected-note@-1 
{{initializer of 'trident' is not a constant expression}}

>From 3c944c05fded091f9488a9786bc80c57e291b6ed Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Mon, 29 Jul 2024 10:12:34 -0700
Subject: [PATCH 02/10] - Add memory management for APValues we create for
 unknown references and pointers - Fix handling for comparing unknown pointers
 - Add more tests

---
 clang/include/clang/AST/APValue.h             |  2 +-
 clang/lib/AST/ExprConstant.cpp                | 27 +++++++-
 .../SemaCXX/constant-expression-p2280r4.cpp   | 64 +++++++++++++++++++
 3 files changed, 91 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index 6352348107a647..16153c166e23c8 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -257,7 +257,7 @@ class APValue {
 
 private:
   ValueKind Kind;
-  bool AllowConstexprUnknown = false;
+  bool AllowConstexprUnknown;
 
   struct ComplexAPSInt {
     APSInt Real, Imag;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 9a427d908e4c91..a7e2cc455e65d2 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -555,6 +555,7 @@ namespace {
     typedef std::map<MapKeyTy, APValue> MapTy;
     /// Temporaries - Temporary lvalues materialized within this stack frame.
     MapTy Temporaries;
+    MapTy ConstexprUnknownAPValues;
 
     /// CallRange - The source range of the call expression for this call.
     SourceRange CallRange;
@@ -629,6 +630,9 @@ namespace {
     APValue &createTemporary(const KeyT *Key, QualType T,
                              ScopeKind Scope, LValue &LV);
 
+    APValue &createConstexprUnknownAPValues(const VarDecl *Key,
+                                            APValue::LValueBase Base);
+
     /// Allocate storage for a parameter of a function call made in this frame.
     APValue &createParam(CallRef Args, const ParmVarDecl *PVD, LValue &LV);
 
@@ -1925,6 +1929,16 @@ APValue &CallStackFrame::createTemporary(const KeyT 
*Key, QualType T,
   return createLocal(Base, Key, T, Scope);
 }
 
+APValue &
+CallStackFrame::createConstexprUnknownAPValues(const VarDecl *Key,
+                                               APValue::LValueBase Base) {
+  APValue &Result = ConstexprUnknownAPValues[MapKeyTy(Key, Base.getVersion())];
+  Result = APValue(Base, APValue::ConstexprUnknown{}, CharUnits::One());
+  Result.setConstexprUnknown();
+
+  return Result;
+}
+
 /// Allocate storage for a parameter of a function call made in this frame.
 APValue &CallStackFrame::createParam(CallRef Args, const ParmVarDecl *PVD,
                                      LValue &LV) {
@@ -3410,6 +3424,10 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
   // P2280R4 struck the initialization requirement for variables of reference
   // type so we can no longer assume we have an Init.
   if (Init && !VD->evaluateValue()) {
+    if (AllowConstexprUnknown) {
+      Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base);
+      return true;
+    }
     Info.FFDiag(E, diag::note_constexpr_var_init_non_constant, 1) << VD;
     NoteLValueLocation(Info, Base);
     return false;
@@ -3447,7 +3465,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
   // a ConstexprUnknown status.
   if (AllowConstexprUnknown) {
     if (!Result) {
-      Result = new APValue(Base, APValue::ConstexprUnknown{}, 
CharUnits::One());
+      Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base);
+    } else {
+      Result->setConstexprUnknown();
     }
   }
   return true;
@@ -13633,6 +13653,11 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const 
BinaryOperator *E,
     if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK)
       return false;
 
+    // If we have Unknown pointers we should fail if they are not global 
values.
+    if (!(IsGlobalLValue(LHSValue.getLValueBase()) && 
IsGlobalLValue(RHSValue.getLValueBase())) &&
+         (LHSValue.AllowConstexprUnknown || RHSValue.AllowConstexprUnknown))
+         return false;
+
     // Reject differing bases from the normal codepath; we special-case
     // comparisons to null.
     if (!HasSameBase(LHSValue, RHSValue)) {
diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp 
b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
index be41eed88c4944..0a32a9f0d7aca8 100644
--- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -52,3 +52,67 @@ extern Swim& trident; // expected-note {{declared here}}
 constexpr auto& sandeno   = typeid(dc);         // ok: can only be typeid(Swim)
 constexpr auto& gallagher = typeid(trident);    // expected-error {{constexpr 
variable 'gallagher' must be initialized by a constant expression}}
                                                 // expected-note@-1 
{{initializer of 'trident' is not a constant expression}}
+
+namespace GH64376 {
+template<int V>
+struct Test {
+    static constexpr int value = V;
+};
+
+int main() {
+    Test<124> test;
+    auto& test2 = test;
+
+    if constexpr(test2.value > 3) {
+       return 1;
+    }
+
+    return 0;
+}
+}
+
+namespace GH30060 {
+template<int V>
+struct A {
+  static constexpr int value = V;
+};
+
+template<class T>
+static void test1(T &f) {
+    A<f.value> bar;
+}
+
+void g() {
+    A<42> f;
+
+    test1(f);
+}
+}
+
+namespace GH26067 {
+struct A {
+    constexpr operator int() const { return 42; }
+};
+
+template <int>
+void f() {}
+
+void test(const A& value) {
+    f<value>();
+}
+
+int main() {
+    A a{};
+    test(a);
+}
+}
+
+namespace GH34365 {
+void g() {
+  auto f = []() { return 42; };
+  constexpr int x = f();
+  [](auto f) { constexpr int x = f(); }(f);
+  [](auto &f) { constexpr int x = f(); }(f);
+  (void)[&]() { constexpr int x = f(); };
+}
+}

>From 0c27e1ec1735487940bdaea7cb547c9b1ddc737d Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Mon, 29 Jul 2024 10:55:46 -0700
Subject: [PATCH 03/10] clang-format

---
 clang/lib/AST/ExprConstant.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index a7e2cc455e65d2..424df336f20e83 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13654,9 +13654,10 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const 
BinaryOperator *E,
       return false;
 
     // If we have Unknown pointers we should fail if they are not global 
values.
-    if (!(IsGlobalLValue(LHSValue.getLValueBase()) && 
IsGlobalLValue(RHSValue.getLValueBase())) &&
-         (LHSValue.AllowConstexprUnknown || RHSValue.AllowConstexprUnknown))
-         return false;
+    if (!(IsGlobalLValue(LHSValue.getLValueBase()) &&
+          IsGlobalLValue(RHSValue.getLValueBase())) &&
+        (LHSValue.AllowConstexprUnknown || RHSValue.AllowConstexprUnknown))
+      return false;
 
     // Reject differing bases from the normal codepath; we special-case
     // comparisons to null.

>From 88c701fc6577794f60cb6b6841a27aa95cdd9f1c Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Mon, 29 Jul 2024 15:52:12 -0700
Subject: [PATCH 04/10] Fix CheckedHandleSizeof to handle references, before
 unknown references and pointers it did not have to handle this case before.

---
 clang/lib/AST/ExprConstant.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 424df336f20e83..2423be6a051de6 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -12273,6 +12273,10 @@ static bool determineEndOffset(EvalInfo &Info, 
SourceLocation ExprLoc,
   auto CheckedHandleSizeof = [&](QualType Ty, CharUnits &Result) {
     if (Ty.isNull() || Ty->isIncompleteType() || Ty->isFunctionType())
       return false;
+
+    if (Ty->isReferenceType())
+      Ty = Ty.getNonReferenceType();
+
     return HandleSizeof(Info, ExprLoc, Ty, Result);
   };
 

>From 16c44b48c9a5c41a2de3976b4e23c13e62918da7 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Tue, 30 Jul 2024 18:39:55 -0700
Subject: [PATCH 05/10] Address comments by making AllowConstexprUnknown to be
 a bit-field and updating comments to refer to the standard when possible

---
 clang/include/clang/AST/APValue.h |  2 +-
 clang/lib/AST/APValue.cpp         |  5 ++++-
 clang/lib/AST/ExprConstant.cpp    | 33 +++++++++++++++++--------------
 3 files changed, 23 insertions(+), 17 deletions(-)

diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index 16153c166e23c8..625d8c2e4c846b 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -257,7 +257,7 @@ class APValue {
 
 private:
   ValueKind Kind;
-  bool AllowConstexprUnknown;
+  bool AllowConstexprUnknown : 1;
 
   struct ComplexAPSInt {
     APSInt Real, Imag;
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index f7841cb6556de7..dbab6041e9e226 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -475,7 +475,10 @@ bool APValue::needsCleanup() const {
 void APValue::swap(APValue &RHS) {
   std::swap(Kind, RHS.Kind);
   std::swap(Data, RHS.Data);
-  std::swap(AllowConstexprUnknown, RHS.AllowConstexprUnknown);
+  // We can't use std::swap w/ bit-fields
+  bool tmp = AllowConstexprUnknown;
+  AllowConstexprUnknown = RHS.AllowConstexprUnknown;
+  RHS.AllowConstexprUnknown = tmp;
 }
 
 /// Profile the value of an APInt, excluding its bit-width.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 2423be6a051de6..a78269e4b63a60 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -3322,8 +3322,8 @@ static bool HandleLValueComplexElement(EvalInfo &Info, 
const Expr *E,
 static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
                                 const VarDecl *VD, CallStackFrame *Frame,
                                 unsigned Version, APValue *&Result) {
-  // P2280R4 If we have a reference type and we are in C++23 allow unknown
-  // references and pointers.
+  // C++23 [expr.const]p8 If we have a reference type allow unknown references
+  // and pointers.
   bool AllowConstexprUnknown =
       Info.getLangOpts().CPlusPlus23 && VD->getType()->isReferenceType();
 
@@ -3361,8 +3361,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
     return true;
   }
 
-  // P2280R4 struck the restriction that variable of referene type lifetime
+  // P2280R4 struck the restriction that variable of reference type lifetime
   // should begin within the evaluation of E
+  // Used to be C++20 [expr.const]p5.12.2:
   if (isa<ParmVarDecl>(VD) && !AllowConstexprUnknown) {
     // Assume parameters of a potential constant expression are usable in
     // constant expressions.
@@ -3387,8 +3388,9 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
   // FIXME: We should eventually check whether the variable has a reachable
   // initializing declaration.
   const Expr *Init = VD->getAnyInitializer(VD);
-  // P2280R4 struck the restriction that variable of referene type should have
+  // P2280R4 struck the restriction that variable of reference type should have
   // a preceding initialization.
+  // Used to be C++20 [expr.const]p5.12:
   if (!Init && !AllowConstexprUnknown) {
     // Don't diagnose during potential constant expression checking; an
     // initializer might be added later.
@@ -3750,8 +3752,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const 
CompleteObject &Obj,
   const FieldDecl *LastField = nullptr;
   const FieldDecl *VolatileField = nullptr;
 
-  // P2280R4 If we have an unknown referene or pointer and we don't have a
-  // value then bail out.
+  // C++23 [expr.const]p8 If we have an unknown reference or pointers and it
+  // does not have a value then bail out.
   if (O->allowConstexprUnknown() && !O->hasValue())
     return false;
 
@@ -5788,7 +5790,7 @@ struct CheckDynamicTypeHandler {
 static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This,
                              AccessKinds AK, bool Polymorphic) {
   // P2280R4 We are not allowed to invoke a virtual function whose dynamic type
-  // us constexpr-unknown, so stop early and let this fail later on if we
+  // is constexpr-unknown, so stop early and let this fail later on if we
   // attempt to do so.
   if (This.allowConstexprUnknown())
     return true;
@@ -8663,8 +8665,8 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const 
DeclRefExpr *E) {
 
 
 bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
-  // P2280R4 if we are in C++23 track if we have an unknown reference or
-  // pointer.
+  // C++23 [expr.const]p8 If we have a reference type allow unknown references
+  // and pointers.
   bool AllowConstexprUnknown =
       Info.getLangOpts().CPlusPlus23 && VD->getType()->isReferenceType();
   // If we are within a lambda's call operator, check whether the 'VD' referred
@@ -8736,13 +8738,14 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, 
const VarDecl *VD) {
   if (!V->hasValue()) {
     // FIXME: Is it possible for V to be indeterminate here? If so, we should
     // adjust the diagnostic to say that.
-    // P2280R4 If we are have a variable that is unknown reference or pointer
-    // it may not have a value but still be usable later on so do not diagnose.
+    // C++23 [expr.const]p8 If we have a variable that is unknown reference
+    // or pointer it may not have a value but still be usable later on so do 
not
+    // diagnose.
     if (!Info.checkingPotentialConstantExpression() && !AllowConstexprUnknown)
       Info.FFDiag(E, diag::note_constexpr_use_uninit_reference);
 
-    // P2280R4 If we are have a variable that is unknown reference or pointer
-    // try to recover it from the frame and set the result accordingly.
+    // C++23 [expr.const]p8 If we have a variable that is unknown reference or
+    // pointer try to recover it from the frame and set the result accordingly.
     if (VD->getType()->isReferenceType() && AllowConstexprUnknown) {
       if (Frame) {
         Result.set({VD, Frame->Index, Version});
@@ -11570,8 +11573,8 @@ class IntExprEvaluator
   }
 
   bool Success(const APValue &V, const Expr *E) {
-    // P2280R4 if we have an unknown reference or pointer allow further
-    // evaluation of the value.
+    // C++23 [expr.const]p8 If we have a variable that is unknown reference or
+    // pointer allow further evaluation of the value.
     if (V.isLValue() || V.isAddrLabelDiff() || V.isIndeterminate() ||
         V.allowConstexprUnknown()) {
       Result = V;

>From 8f9977160ab25de15c028ec068b77781d3749fb2 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Wed, 18 Dec 2024 17:32:12 -0800
Subject: [PATCH 06/10] Fixing typos in merge conflict resolution

---
 clang/include/clang/AST/APValue.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index 0d58fa3ef202ec..66058acaef79d2 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -356,7 +356,7 @@ class APValue {
   APValue(LValueBase Base, const CharUnits &Offset, NoLValuePath,
           bool IsNullPtr = false)
       : Kind(None), AllowConstexprUnknown(false) {
-    MakeLValue(); setLValue(B, O, N, IsNullPtr);
+    MakeLValue(); setLValue(Base, Offset, NoLValuePath{}, IsNullPtr);
   }
   /// Creates an lvalue APValue with an lvalue path.
   /// \param Base The base of the lvalue.

>From 1e2fdbd3e178325f5e8e0608107648413f6b5643 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Wed, 18 Dec 2024 17:51:08 -0800
Subject: [PATCH 07/10] Fix clang-format issues

---
 clang/include/clang/AST/APValue.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index 66058acaef79d2..5eb2f07ff8336c 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -356,7 +356,8 @@ class APValue {
   APValue(LValueBase Base, const CharUnits &Offset, NoLValuePath,
           bool IsNullPtr = false)
       : Kind(None), AllowConstexprUnknown(false) {
-    MakeLValue(); setLValue(Base, Offset, NoLValuePath{}, IsNullPtr);
+    MakeLValue();
+    setLValue(Base, Offset, NoLValuePath{}, IsNullPtr);
   }
   /// Creates an lvalue APValue with an lvalue path.
   /// \param Base The base of the lvalue.

>From d1febe81dd74d9621d99cca1c9c954559c24eb66 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Wed, 18 Dec 2024 17:58:31 -0800
Subject: [PATCH 08/10] More clang-format fixes

---
 clang/include/clang/AST/APValue.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index 5eb2f07ff8336c..8bb8012909c27c 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -369,7 +369,8 @@ class APValue {
   APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path,
           bool OnePastTheEnd, bool IsNullPtr = false)
       : Kind(None), AllowConstexprUnknown(false) {
-    MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, IsNullPtr);
+    MakeLValue();
+    setLValue(B, O, Path, OnePastTheEnd, IsNullPtr);
   }
 
   APValue(LValueBase B, ConstexprUnknown, const CharUnits &O,
@@ -392,7 +393,8 @@ class APValue {
   /// \param UninitStruct Marker. Pass an empty UninitStruct.
   /// \param NumBases Number of bases.
   /// \param NumMembers Number of members.
-  APValue(UninitStruct, unsigned NumBases, unsigned NumMembers) : Kind(None), 
AllowConstexprUnknown(false) {
+  APValue(UninitStruct, unsigned NumBases, unsigned NumMembers)
+      : Kind(None), AllowConstexprUnknown(false) {
     MakeStruct(NumBases, NumMembers);
   }
   /// Creates a new union APValue.

>From 9dd79589acef2e4e531528e6327ce2d27e35d5fa Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Thu, 19 Dec 2024 09:45:20 -0800
Subject: [PATCH 09/10] Fix for EvaluateBuiltinIsWithinLifetime when dealing
 wint constexpr unknown variables

---
 clang/lib/AST/ExprConstant.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 304fc170974db1..4431468950b0b0 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17934,6 +17934,9 @@ std::optional<bool> 
EvaluateBuiltinIsWithinLifetime(IntExprEvaluator &IEE,
   if (!EvaluatePointer(Arg, Val, Info))
     return std::nullopt;
 
+  if (Val.allowConstexprUnknown())
+    return true;
+
   auto Error = [&](int Diag) {
     bool CalledFromStd = false;
     const auto *Callee = Info.CurrentCall->getCallee();

>From e4225df2ffe65cc09c2609759051bcdd4f831291 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghm...@intel.com>
Date: Thu, 19 Dec 2024 17:09:38 -0800
Subject: [PATCH 10/10] Adding some more tests, did some minor documentation
 fixes for APValue

---
 clang/include/clang/AST/APValue.h             | 16 +++++---
 clang/lib/AST/ExprConstant.cpp                |  2 +-
 .../SemaCXX/constant-expression-p2280r4.cpp   | 38 +++++++++++++++++++
 3 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index 8bb8012909c27c..6d7c214e2eb44f 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -366,18 +366,22 @@ class APValue {
   /// \param OnePastTheEnd Whether this lvalue is one-past-the-end of the
   /// subobject it points to.
   /// \param IsNullPtr Whether this lvalue is a null pointer.
-  APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path,
-          bool OnePastTheEnd, bool IsNullPtr = false)
+  APValue(LValueBase Base, const CharUnits &Offset,
+          ArrayRef<LValuePathEntry> Path, bool OnePastTheEnd,
+          bool IsNullPtr = false)
       : Kind(None), AllowConstexprUnknown(false) {
     MakeLValue();
-    setLValue(B, O, Path, OnePastTheEnd, IsNullPtr);
+    setLValue(Base, Offset, Path, OnePastTheEnd, IsNullPtr);
   }
-
-  APValue(LValueBase B, ConstexprUnknown, const CharUnits &O,
+  /// Creates a constexpr unknown lvalue APValue.
+  /// \param Base The base of the lvalue.
+  /// \param Offset The offset of the lvalue.
+  /// \param IsNullPtr Whether this lvalue is a null pointer.
+  APValue(LValueBase Base, const CharUnits &Offset, ConstexprUnknown,
           bool IsNullPtr = false)
       : Kind(None), AllowConstexprUnknown(true) {
     MakeLValue();
-    setLValue(B, O, NoLValuePath{}, IsNullPtr);
+    setLValue(Base, Offset, NoLValuePath{}, IsNullPtr);
   }
 
   /// Creates a new array APValue.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4431468950b0b0..a4adc15fd7dac9 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1960,7 +1960,7 @@ APValue &
 CallStackFrame::createConstexprUnknownAPValues(const VarDecl *Key,
                                                APValue::LValueBase Base) {
   APValue &Result = ConstexprUnknownAPValues[MapKeyTy(Key, Base.getVersion())];
-  Result = APValue(Base, APValue::ConstexprUnknown{}, CharUnits::One());
+  Result = APValue(Base, CharUnits::One(), APValue::ConstexprUnknown{});
   Result.setConstexprUnknown();
 
   return Result;
diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp 
b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
index 0a32a9f0d7aca8..0f85c60629eed9 100644
--- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -53,6 +53,26 @@ constexpr auto& sandeno   = typeid(dc);         // ok: can 
only be typeid(Swim)
 constexpr auto& gallagher = typeid(trident);    // expected-error {{constexpr 
variable 'gallagher' must be initialized by a constant expression}}
                                                 // expected-note@-1 
{{initializer of 'trident' is not a constant expression}}
 
+namespace explicitThis {
+struct C {
+  constexpr int b()  { return 0; };
+
+  constexpr int f(this C &c) {
+    return c.b();     // ok
+  }
+
+   constexpr int g() {
+    return f();       // ok
+  }
+};
+
+void g() {
+  C c;
+  constexpr int x = c.f();
+  constexpr int y = c.g();
+}
+}
+
 namespace GH64376 {
 template<int V>
 struct Test {
@@ -116,3 +136,21 @@ void g() {
   (void)[&]() { constexpr int x = f(); };
 }
 }
+
+namespace GH118063 {
+template <unsigned int N>
+struct array {
+    constexpr auto size() const -> unsigned int {
+        return N;
+    }
+};
+
+constexpr auto f(array<5> const& arr) {
+    return array<arr.size()>{}.size();
+}
+
+int g() {
+    array<5> arr {};
+    static_assert(f(arr) == 5);
+}
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to