@@ -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
@@ -11684,6 +11698,9 @@ class IntExprEvaluator
11684
11698
11685
11699
bool ZeroInitialization(const Expr *E) { return Success(0, E); }
11686
11700
11701
+ friend std::optional<bool> EvaluateBuiltinIsWithinLifetime(IntExprEvaluator &,
11702
+ const CallExpr *);
11703
+
11687
11704
//===--------------------------------------------------------------------===//
11688
11705
// Visitor Methods
11689
11706
//===--------------------------------------------------------------------===//
@@ -12743,6 +12760,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
12743
12760
return Success(Info.InConstantContext, E);
12744
12761
}
12745
12762
12763
+ case Builtin::BI__builtin_is_within_lifetime:
12764
+ if (auto result = EvaluateBuiltinIsWithinLifetime(*this, E))
12765
+ return Success(*result, E);
12766
+ return false;
12767
+
12746
12768
case Builtin::BI__builtin_ctz:
12747
12769
case Builtin::BI__builtin_ctzl:
12748
12770
case Builtin::BI__builtin_ctzll:
@@ -17322,3 +17344,84 @@ bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const {
17322
17344
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
17323
17345
return EvaluateBuiltinStrLen(this, Result, Info);
17324
17346
}
17347
+
17348
+ namespace {
17349
+ struct IsWithinLifetimeHandler {
17350
+ EvalInfo &Info;
17351
+ static constexpr AccessKinds AccessKind = AccessKinds::AK_IsWithinLifetime;
17352
+ using result_type = std::optional<bool>;
17353
+ std::optional<bool> failed() { return std::nullopt; }
17354
+ template <typename T>
17355
+ std::optional<bool> found(T &Subobj, QualType SubobjType) {
17356
+ return true;
17357
+ }
17358
+ };
17359
+
17360
+ std::optional<bool> EvaluateBuiltinIsWithinLifetime(IntExprEvaluator &IEE,
17361
+ const CallExpr *E) {
17362
+ EvalInfo &Info = IEE.Info;
17363
+ // Sometimes this is called during some sorts of constant folding / early
17364
+ // evaluation. These are meant for non-constant expressions and are not
17365
+ // necessary since this consteval builtin will never be evaluated at runtime.
17366
+ // Just fail to evaluate when not in a constant context.
17367
+ if (!Info.InConstantContext)
17368
+ return std::nullopt;
17369
+ assert(E->getBuiltinCallee() == Builtin::BI__builtin_is_within_lifetime);
17370
+ const Expr *Arg = E->getArg(0);
17371
+ if (Arg->isValueDependent())
17372
+ return std::nullopt;
17373
+ LValue Val;
17374
+ if (!EvaluatePointer(Arg, Val, Info))
17375
+ return std::nullopt;
17376
+
17377
+ auto Error = [&](int Diag) {
17378
+ bool CalledFromStd = false;
17379
+ const auto *Callee = Info.CurrentCall->getCallee();
17380
+ if (Callee && Callee->isInStdNamespace()) {
17381
+ const IdentifierInfo *Identifier = Callee->getIdentifier();
17382
+ CalledFromStd = Identifier && Identifier->isStr("is_within_lifetime");
17383
+ }
17384
+ Info.CCEDiag(CalledFromStd ? Info.CurrentCall->getCallRange().getBegin()
17385
+ : E->getExprLoc(),
17386
+ diag::err_invalid_is_within_lifetime)
17387
+ << (CalledFromStd ? "std::is_within_lifetime"
17388
+ : "__builtin_is_within_lifetime")
17389
+ << Diag;
17390
+ return std::nullopt;
17391
+ };
17392
+ // C++2c [meta.const.eval]p4:
17393
+ // During the evaluation of an expression E as a core constant expression, a
17394
+ // call to this function is ill-formed unless p points to an object that is
17395
+ // usable in constant expressions or whose complete object's lifetime began
17396
+ // within E.
17397
+
17398
+ // Make sure it points to an object
17399
+ // nullptr does not point to an object
17400
+ if (Val.isNullPointer() || Val.getLValueBase().isNull())
17401
+ return Error(0);
17402
+ QualType T = Val.getLValueBase().getType();
17403
+ assert(!T->isFunctionType() &&
17404
+ "Pointers to functions should have been typed as function pointers "
17405
+ "which would have been rejected earlier");
17406
+ assert(T->isObjectType());
17407
+ // Hypothetical array element is not an object
17408
+ if (Val.getLValueDesignator().isOnePastTheEnd())
17409
+ return Error(1);
17410
+ assert(Val.getLValueDesignator().isValidSubobject() &&
17411
+ "Unchecked case for valid subobject");
17412
+ // All other ill-formed values should have failed EvaluatePointer, so the
17413
+ // object should be a pointer to an object that is usable in a constant
17414
+ // expression or whose complete lifetime began within the expression
17415
+ CompleteObject CO =
17416
+ findCompleteObject(Info, E, AccessKinds::AK_IsWithinLifetime, Val, T);
17417
+ // The lifetime hasn't begun yet if we are still evaluating the
17418
+ // initializer ([basic.life]p(1.2))
17419
+ if (Info.EvaluatingDeclValue && CO.Value == Info.EvaluatingDeclValue)
17420
+ return Error(2);
17421
+
17422
+ if (!CO)
17423
+ return false;
17424
+ IsWithinLifetimeHandler handler{Info};
17425
+ return findSubobject(Info, E, CO, Val.getLValueDesignator(), handler);
17426
+ }
17427
+ } // namespace
0 commit comments