Author: Shafik Yaghmour Date: 2025-01-22T09:28:08+01:00 New Revision: 0a9c08c59ba61e727e9dee6d71883d9106963442
URL: https://github.com/llvm/llvm-project/commit/0a9c08c59ba61e727e9dee6d71883d9106963442 DIFF: https://github.com/llvm/llvm-project/commit/0a9c08c59ba61e727e9dee6d71883d9106963442.diff LOG: [Clang] Implement P2280R4 Using unknown pointers and references in constant expressions (#95474) 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. Fixes: https://github.com/llvm/llvm-project/issues/63139 https://github.com/llvm/llvm-project/issues/63117 Added: clang/test/SemaCXX/constant-expression-p2280r4.cpp Modified: clang/docs/ReleaseNotes.rst clang/include/clang/AST/APValue.h clang/lib/AST/APValue.cpp clang/lib/AST/ExprConstant.cpp clang/test/SemaCXX/constant-expression-cxx11.cpp clang/test/SemaCXX/constant-expression-cxx2a.cpp clang/www/cxx_status.html Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6895fca41c3bd3..cad17c1b3957b6 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -318,6 +318,8 @@ C++23 Feature Support - ``__cpp_explicit_this_parameter`` is now defined. (#GH82780) +- Add support for `P2280R4 Using unknown pointers and references in constant expressions <https://wg21.link/P2280R4>`_. (#GH63139) + C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index 4401f3a8ff482c..833a78c77871d0 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -247,6 +247,7 @@ class APValue { struct NoLValuePath {}; struct UninitArray {}; struct UninitStruct {}; + struct ConstexprUnknown {}; template <typename Impl> friend class clang::serialization::BasicReaderBase; friend class ASTImporter; @@ -254,6 +255,7 @@ class APValue { private: ValueKind Kind; + bool AllowConstexprUnknown : 1; struct ComplexAPSInt { APSInt Real, Imag; @@ -312,32 +314,39 @@ class APValue { DataType Data; public: + bool allowConstexprUnknown() const { return AllowConstexprUnknown; } + + void setConstexprUnknown(bool IsConstexprUnknown = true) { + AllowConstexprUnknown = IsConstexprUnknown; + } + /// Creates an empty APValue of type None. - APValue() : Kind(None) {} + APValue() : Kind(None), AllowConstexprUnknown(false) {} /// Creates an integer APValue holding the given value. - explicit APValue(APSInt I) : Kind(None) { + explicit APValue(APSInt I) : Kind(None), AllowConstexprUnknown(false) { MakeInt(); setInt(std::move(I)); } /// Creates a float APValue holding the given value. - explicit APValue(APFloat F) : Kind(None) { + explicit APValue(APFloat F) : Kind(None), AllowConstexprUnknown(false) { MakeFloat(); setFloat(std::move(F)); } /// Creates a fixed-point APValue holding the given value. - explicit APValue(APFixedPoint FX) : Kind(None) { + explicit APValue(APFixedPoint FX) : Kind(None), AllowConstexprUnknown(false) { MakeFixedPoint(std::move(FX)); } /// Creates a vector APValue with \p N elements. The elements /// are read from \p E. - explicit APValue(const APValue *E, unsigned N) : Kind(None) { + explicit APValue(const APValue *E, unsigned N) + : Kind(None), AllowConstexprUnknown(false) { MakeVector(); setVector(E, N); } /// Creates an integer complex APValue with the given real and imaginary /// values. - APValue(APSInt R, APSInt I) : Kind(None) { + APValue(APSInt R, APSInt I) : Kind(None), AllowConstexprUnknown(false) { MakeComplexInt(); setComplexInt(std::move(R), std::move(I)); } /// Creates a float complex APValue with the given real and imaginary values. - 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); @@ -348,7 +357,7 @@ class APValue { /// \param IsNullPtr Whether this lvalue is a null pointer. APValue(LValueBase Base, const CharUnits &Offset, NoLValuePath, bool IsNullPtr = false) - : Kind(None) { + : Kind(None), AllowConstexprUnknown(false) { MakeLValue(); setLValue(Base, Offset, NoLValuePath{}, IsNullPtr); } @@ -362,23 +371,36 @@ class APValue { APValue(LValueBase Base, const CharUnits &Offset, ArrayRef<LValuePathEntry> Path, bool OnePastTheEnd, bool IsNullPtr = false) - : Kind(None) { + : Kind(None), AllowConstexprUnknown(false) { MakeLValue(); setLValue(Base, Offset, Path, OnePastTheEnd, IsNullPtr); } + /// 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(Base, Offset, NoLValuePath{}, IsNullPtr); + } + /// Creates a new array APValue. /// \param UninitArray Marker. Pass an empty UninitArray. /// \param InitElts Number of elements you're going to initialize in the /// array. /// \param Size Full size of the array. - APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(None) { + APValue(UninitArray, unsigned InitElts, unsigned Size) + : Kind(None), AllowConstexprUnknown(false) { MakeArray(InitElts, Size); } /// Creates a new struct 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) { + APValue(UninitStruct, unsigned NumBases, unsigned NumMembers) + : Kind(None), AllowConstexprUnknown(false) { MakeStruct(NumBases, NumMembers); } /// Creates a new union APValue. @@ -386,7 +408,7 @@ class APValue { /// \param ActiveValue The value of the active union member. explicit APValue(const FieldDecl *ActiveDecl, const APValue &ActiveValue = APValue()) - : Kind(None) { + : Kind(None), AllowConstexprUnknown(false) { MakeUnion(); setUnion(ActiveDecl, ActiveValue); } @@ -395,14 +417,15 @@ class APValue { /// \param IsDerivedMember Whether member is a derived one. /// \param Path The path of the member. APValue(const ValueDecl *Member, bool IsDerivedMember, - ArrayRef<const CXXRecordDecl*> Path) : Kind(None) { + ArrayRef<const CXXRecordDecl *> Path) + : Kind(None), AllowConstexprUnknown(false) { MakeMemberPointer(Member, IsDerivedMember, Path); } /// Creates a new address label diff APValue. /// \param LHSExpr The left-hand side of the diff erence. /// \param RHSExpr The right-hand side of the diff erence. - 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 f9e08b70d6ab0a..3b814be2663307 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,10 @@ bool APValue::needsCleanup() const { void APValue::swap(APValue &RHS) { std::swap(Kind, RHS.Kind); std::swap(Data, RHS.Data); + // 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 2e680d1569f60f..734311e5d8b9aa 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -572,6 +572,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; @@ -646,6 +647,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); @@ -1630,8 +1634,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; } @@ -1649,6 +1656,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?"); @@ -1657,6 +1666,7 @@ namespace { InvalidBase = false; Designator = SubobjectDesignator(Ctx, V); IsNullPtr = V.isNullPointer(); + AllowConstexprUnknown = V.allowConstexprUnknown(); } void set(APValue::LValueBase B, bool BInvalid = false) { @@ -1674,6 +1684,7 @@ namespace { InvalidBase = BInvalid; Designator = SubobjectDesignator(getType(B)); IsNullPtr = false; + AllowConstexprUnknown = false; } void setNull(ASTContext &Ctx, QualType PointerTy) { @@ -1683,6 +1694,7 @@ namespace { InvalidBase = false; Designator = SubobjectDesignator(PointerTy->getPointeeType()); IsNullPtr = true; + AllowConstexprUnknown = false; } void setInvalid(APValue::LValueBase B, unsigned I = 0) { @@ -1944,6 +1956,15 @@ 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, CharUnits::One(), APValue::ConstexprUnknown{}); + + 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) { @@ -3446,6 +3467,11 @@ static bool HandleLValueVectorElement(EvalInfo &Info, const Expr *E, static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, const VarDecl *VD, CallStackFrame *Frame, unsigned Version, APValue *&Result) { + // C++23 [expr.const]p8 If we have a reference type 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. @@ -3480,7 +3506,11 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, return true; } - if (isa<ParmVarDecl>(VD)) { + // 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: + // ... its lifetime began 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() || @@ -3504,7 +3534,11 @@ 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 reference type should have + // a preceding initialization. + // Used to be C++20 [expr.const]p5.12: + // ... reference has a preceding initialization and either ... + if (!Init && !AllowConstexprUnknown) { // Don't diagnose during potential constant expression checking; an // initializer might be added later. if (!Info.checkingPotentialConstantExpression()) { @@ -3515,7 +3549,11 @@ 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. + // Used to be C++20 [expr.const]p5.12: + // ... reference has a preceding initialization and either ... + 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 @@ -3534,7 +3572,15 @@ 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. + // Used to be C++20 [expr.const]p5.12: + // ... reference has a preceding initialization and either ... + 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; @@ -3566,6 +3612,20 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, } Result = VD->getEvaluatedValue(); + + // C++23 [expr.const]p8 + // ... For such an object that is not usable in constant expressions, the + // dynamic type of the object is constexpr-unknown. For such a reference that + // is not usable in constant expressions, the reference is treated as binding + // to an unspecified object of the referenced type whose lifetime and that of + // all subobjects includes the entire constant evaluation and whose dynamic + // type is constexpr-unknown. + if (AllowConstexprUnknown) { + if (!Result) + Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base); + else + Result->setConstexprUnknown(); + } return true; } @@ -3847,6 +3907,11 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, const FieldDecl *LastField = nullptr; const FieldDecl *VolatileField = nullptr; + // 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; + // 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. @@ -5909,6 +5974,15 @@ struct CheckDynamicTypeHandler { /// dynamic type. static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This, AccessKinds AK, bool Polymorphic) { + // We are not allowed to invoke a virtual function whose dynamic type + // is constexpr-unknown, so stop early and let this fail later on if we + // attempt to do so. + // C++23 [expr.const]p5.6 + // an invocation of a virtual function ([class.virtual]) for an object whose + // dynamic type is constexpr-unknown; + if (This.allowConstexprUnknown()) + return true; + if (This.Designator.Invalid) return false; @@ -5981,7 +6055,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 @@ -8749,7 +8829,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 = @@ -8805,7 +8886,10 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { - + // 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 // 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 @@ -8875,10 +8959,24 @@ 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()) + // 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); + + // 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}); + return true; + } + return Success(VD); + } return false; } + return Success(*V, E); } @@ -11882,7 +11980,10 @@ class IntExprEvaluator } bool Success(const APValue &V, const Expr *E) { - if (V.isLValue() || V.isAddrLabelDiff() || V.isIndeterminate()) { + // 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; return true; } @@ -12597,6 +12698,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); }; @@ -14266,6 +14371,12 @@ 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 diff ering bases from the normal codepath; we special-case // comparisons to null. if (!HasSameBase(LHSValue, RHSValue)) { @@ -17859,6 +17970,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(); diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index c990dc78deb9b3..a1234b67acd6dc 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -1471,9 +1471,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}} }; @@ -2007,7 +2007,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 diff er}} 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}} } @@ -2427,15 +2428,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); } @@ -2444,8 +2445,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 36d4d25c48471b..85720606fe9def 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..0f85c60629eed9 --- /dev/null +++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp @@ -0,0 +1,156 @@ +// 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}} + +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 { + 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(); }; +} +} + +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); +} +} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index a8e79cd3475abc..37f7cd0111fc6d 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -439,8 +439,8 @@ <h2 id="cxx23">C++23 implementation status</h2> </tr> <tr> <td>Using unknown pointers and references in constant expressions</td> - <td><a href="https://wg21.link/P2280R4">P2280R4</a> (<a href="#dr">DR</a>)</td> - <td class="none" align="center">No</td> + <td><a href="https://wg21.link/P2280R4">P2280R4</a></td> + <td class="none" align="center">Clang 20</td> </tr> <tr> <td>static <code>operator()</code></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits