@@ -1522,7 +1522,8 @@ CallStackFrame::~CallStackFrame() {
1522
1522
}
1523
1523
1524
1524
static bool isRead(AccessKinds AK) {
1525
- return AK == AK_Read || AK == AK_ReadObjectRepresentation;
1525
+ return AK == AK_Read || AK == AK_ReadObjectRepresentation ||
1526
+ AK == AK_IsWithinLifetime;
1526
1527
}
1527
1528
1528
1529
static bool isModification(AccessKinds AK) {
@@ -1532,6 +1533,7 @@ static bool isModification(AccessKinds AK) {
1532
1533
case AK_MemberCall:
1533
1534
case AK_DynamicCast:
1534
1535
case AK_TypeId:
1536
+ case AK_IsWithinLifetime:
1535
1537
return false;
1536
1538
case AK_Assign:
1537
1539
case AK_Increment:
@@ -1549,7 +1551,8 @@ static bool isAnyAccess(AccessKinds AK) {
1549
1551
1550
1552
/// Is this an access per the C++ definition?
1551
1553
static bool isFormalAccess(AccessKinds AK) {
1552
- return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy;
1554
+ return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy &&
1555
+ AK != AK_IsWithinLifetime;
1553
1556
}
1554
1557
1555
1558
/// Is this kind of axcess valid on an indeterminate object value?
@@ -1561,6 +1564,7 @@ static bool isValidIndeterminateAccess(AccessKinds AK) {
1561
1564
// These need the object's value.
1562
1565
return false;
1563
1566
1567
+ case AK_IsWithinLifetime:
1564
1568
case AK_ReadObjectRepresentation:
1565
1569
case AK_Assign:
1566
1570
case AK_Construct:
@@ -3707,7 +3711,8 @@ struct CompleteObject {
3707
3711
// In C++14 onwards, it is permitted to read a mutable member whose
3708
3712
// lifetime began within the evaluation.
3709
3713
// FIXME: Should we also allow this in C++11?
3710
- if (!Info.getLangOpts().CPlusPlus14)
3714
+ if (!Info.getLangOpts().CPlusPlus14 &&
3715
+ AK != AccessKinds::AK_IsWithinLifetime)
3711
3716
return false;
3712
3717
return lifetimeStartedInEvaluation(Info, Base, /*MutableSubobject*/true);
3713
3718
}
@@ -3760,6 +3765,12 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
3760
3765
if ((O->isAbsent() && !(handler.AccessKind == AK_Construct && I == N)) ||
3761
3766
(O->isIndeterminate() &&
3762
3767
!isValidIndeterminateAccess(handler.AccessKind))) {
3768
+ // Object has ended lifetime.
3769
+ // If I is non-zero, some subobject (member or array element) of a
3770
+ // complete object has ended its lifetime, so this is valid for
3771
+ // IsWithinLifetime, resulting in false.
3772
+ if (I != 0 && handler.AccessKind == AK_IsWithinLifetime)
3773
+ return false;
3763
3774
if (!Info.checkingPotentialConstantExpression())
3764
3775
Info.FFDiag(E, diag::note_constexpr_access_uninit)
3765
3776
<< handler.AccessKind << O->isIndeterminate()
@@ -3927,6 +3938,9 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
3927
3938
// Placement new onto an inactive union member makes it active.
3928
3939
O->setUnion(Field, APValue());
3929
3940
} else {
3941
+ // Pointer to/into inactive union member: Not within lifetime
3942
+ if (handler.AccessKind == AK_IsWithinLifetime)
3943
+ return false;
3930
3944
// FIXME: If O->getUnionValue() is absent, report that there's no
3931
3945
// active union member rather than reporting the prior active union
3932
3946
// member. We'll need to fix nullptr_t to not use APValue() as its
@@ -11667,6 +11681,9 @@ class IntExprEvaluator
11667
11681
11668
11682
bool ZeroInitialization(const Expr *E) { return Success(0, E); }
11669
11683
11684
+ friend std::optional<bool> EvaluateBuiltinIsWithinLifetime(IntExprEvaluator &,
11685
+ const CallExpr *);
11686
+
11670
11687
//===--------------------------------------------------------------------===//
11671
11688
// Visitor Methods
11672
11689
//===--------------------------------------------------------------------===//
@@ -12722,6 +12739,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
12722
12739
return Success(Info.InConstantContext, E);
12723
12740
}
12724
12741
12742
+ case Builtin::BI__builtin_is_within_lifetime:
12743
+ if (auto result = EvaluateBuiltinIsWithinLifetime(*this, E))
12744
+ return Success(*result, E);
12745
+ return false;
12746
+
12725
12747
case Builtin::BI__builtin_ctz:
12726
12748
case Builtin::BI__builtin_ctzl:
12727
12749
case Builtin::BI__builtin_ctzll:
@@ -17310,3 +17332,83 @@ bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const {
17310
17332
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
17311
17333
return EvaluateBuiltinStrLen(this, Result, Info);
17312
17334
}
17335
+
17336
+ namespace {
17337
+ struct IsWithinLifetimeHandler {
17338
+ EvalInfo &Info;
17339
+ static constexpr AccessKinds AccessKind = AccessKinds::AK_IsWithinLifetime;
17340
+ using result_type = std::optional<bool>;
17341
+ std::optional<bool> failed() { return std::nullopt; }
17342
+ template <typename T>
17343
+ std::optional<bool> found(T &Subobj, QualType SubobjType) {
17344
+ return true;
17345
+ }
17346
+ };
17347
+
17348
+ std::optional<bool> EvaluateBuiltinIsWithinLifetime(IntExprEvaluator &IEE,
17349
+ const CallExpr *E) {
17350
+ EvalInfo &Info = IEE.Info;
17351
+ // Sometimes this is called during some sorts of constant folding / early
17352
+ // evaluation. These are meant for non-constant expressions and are not
17353
+ // necessary since this consteval builtin will never be evaluated at runtime.
17354
+ // Just fail to evaluate when not in a constant context.
17355
+ if (!Info.InConstantContext)
17356
+ return std::nullopt;
17357
+ assert(E->getBuiltinCallee() == Builtin::BI__builtin_is_within_lifetime);
17358
+ const Expr *Arg = E->getArg(0);
17359
+ if (Arg->isValueDependent())
17360
+ return std::nullopt;
17361
+ LValue Val;
17362
+ if (!EvaluatePointer(Arg, Val, Info))
17363
+ return std::nullopt;
17364
+
17365
+ auto Error = [&](int Diag) {
17366
+ bool CalledFromStd = false;
17367
+ const auto *Callee = Info.CurrentCall->getCallee();
17368
+ if (Callee && Callee->isInStdNamespace()) {
17369
+ const IdentifierInfo *Identifier = Callee->getIdentifier();
17370
+ CalledFromStd = Identifier && Identifier->isStr("is_within_lifetime");
17371
+ }
17372
+ Info.CCEDiag(CalledFromStd ? Info.CurrentCall->getCallRange().getBegin()
17373
+ : E->getExprLoc(),
17374
+ diag::err_invalid_is_within_lifetime)
17375
+ << (CalledFromStd ? "std::is_within_lifetime"
17376
+ : "__builtin_is_within_lifetime")
17377
+ << Diag;
17378
+ return std::nullopt;
17379
+ };
17380
+ // C++2c [meta.const.eval]p4:
17381
+ // During the evaluation of an expression E as a core constant expression, a
17382
+ // call to this function is ill-formed unless p points to an object that is
17383
+ // usable in constant expressions or whose complete object's lifetime began
17384
+ // within E.
17385
+
17386
+ // Make sure it points to an object
17387
+ // nullptr does not point to an object
17388
+ if (Val.isNullPointer() || Val.getLValueBase().isNull())
17389
+ return Error(0);
17390
+ QualType T = Val.getLValueBase().getType();
17391
+ if (T->isFunctionType())
17392
+ return Error(1);
17393
+ assert(T->isObjectType());
17394
+ // Hypothetical array element is not an object
17395
+ if (Val.getLValueDesignator().isOnePastTheEnd())
17396
+ return Error(2);
17397
+ assert(Val.getLValueDesignator().isValidSubobject() &&
17398
+ "Unchecked case for valid subobject");
17399
+ // All other ill-formed values should have failed EvaluatePointer, so the
17400
+ // object should be a pointer to an object that is usable in a constant
17401
+ // expression or whose complete lifetime began within the expression
17402
+ CompleteObject CO =
17403
+ findCompleteObject(Info, E, AccessKinds::AK_IsWithinLifetime, Val, T);
17404
+ // The lifetime hasn't begun yet if we are still evaluating the
17405
+ // initializer ([basic.life]p(1.2))
17406
+ if (Info.EvaluatingDeclValue && CO.Value == Info.EvaluatingDeclValue)
17407
+ return Error(3);
17408
+
17409
+ if (!CO)
17410
+ return false;
17411
+ IsWithinLifetimeHandler handler{Info};
17412
+ return findSubobject(Info, E, CO, Val.getLValueDesignator(), handler);
17413
+ }
17414
+ } // namespace
0 commit comments