================ @@ -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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits