[clang] [Clang] Add __builtin_is_within_lifetime to implement P2641R4's std::is_within_lifetime (PR #91895)
Mital Ashok via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 20 03:26:13 PDT 2024
================
@@ -17264,3 +17288,76 @@ bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const {
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
return EvaluateBuiltinStrLen(this, Result, Info);
}
+
+namespace {
+struct IsWithinLifetimeHandler {
+ EvalInfo &Info;
+ static constexpr AccessKinds AccessKind = AccessKinds::AK_IsWithinLifetime;
+ using result_type = std::optional<bool>;
+ std::optional<bool> failed() { return std::nullopt; }
+ template <typename T>
+ std::optional<bool> found(T &Subobj, QualType SubobjType) {
+ return true;
+ }
+};
+
+std::optional<bool> EvaluateBuiltinIsWithinLifetime(IntExprEvaluator &IEE,
+ const CallExpr *E) {
+ EvalInfo &Info = IEE.Info;
+ // Sometimes this is called during some sorts of constant folding / early
+ // evaluation. These are meant for non-constant expressions and are not
+ // necessary since this consteval builtin will never be evaluated at runtime.
+ // Just fail to evaluate when not in a constant context.
+ if (!Info.InConstantContext)
+ return std::nullopt;
+ 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 && Callee->isInStdNamespace() &&
+ Callee->getIdentifier() &&
+ Callee->getIdentifier()->isStr("is_within_lifetime");
+ Info.report(CalledFromStd ? Info.CurrentCall->getCallRange().getBegin()
+ : E->getExprLoc(),
+ diag::err_invalid_is_within_lifetime)
+ << (CalledFromStd ? "std::is_within_lifetime"
+ : "__builtin_is_within_lifetime")
+ << Diag;
+ return std::nullopt;
+ };
+ // C++2c [meta.const.eval]p4:
+ // During the evaluation of an expression E as a core constant expression, a
+ // call to this function is ill-formed unless p points to an object that is
+ // usable in constant expressions or whose complete object's lifetime began
+ // within E.
+
+ // Make sure it points to an object
+ // nullptr does not point to an object
+ if (Val.isNullPointer() || Val.getLValueBase().isNull())
+ return Error(0);
+ QualType T = Val.getLValueBase().getType();
+ if (T->isFunctionType())
+ return Error(1);
+ assert(T->isObjectType());
+ // Hypothetical array element is not an object
+ if (Val.getLValueDesignator().isOnePastTheEnd())
+ return Error(2);
+ assert(Val.getLValueDesignator().isValidSubobject() &&
+ "Unchecked case for valid subobject");
+ // All other ill-formed values should have failed EvaluatePointer, so the
+ // object should be a pointer to an object that is usable in a constant
+ // expression or whose complete lifetime began within the expression
+ CompleteObject CO =
+ findCompleteObject(Info, E, AccessKinds::AK_IsWithinLifetime, Val, T);
+ if (!CO)
+ return false;
+ IsWithinLifetimeHandler handler{Info};
+ return findSubobject(Info, E, CO, Val.getLValueDesignator(), handler);
----------------
MitalAshok wrote:
Yeah, it's intentional: `std::nullopt` -> this call was ill-formed, `false` -> this is a well-formed call and results in `false`, `true` -> this is a well-formed call that results in `true`. I especially want the second one to return `false` instead of nullopt
https://github.com/llvm/llvm-project/pull/91895
More information about the cfe-commits
mailing list