[clang][bytecode] Use bytecode interpreter in EvaluateCharRangeAsString (#138461)
This was always using the ast walker.
This commit is contained in:
@@ -134,6 +134,85 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ResultT>
|
||||
bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr,
|
||||
const Expr *PtrExpr, ResultT &Result) {
|
||||
assert(Stk.empty());
|
||||
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
|
||||
|
||||
// Evaluate size value.
|
||||
APValue SizeValue;
|
||||
if (!evaluateAsRValue(Parent, SizeExpr, SizeValue))
|
||||
return false;
|
||||
|
||||
if (!SizeValue.isInt())
|
||||
return false;
|
||||
uint64_t Size = SizeValue.getInt().getZExtValue();
|
||||
|
||||
auto PtrRes = C.interpretAsPointer(PtrExpr, [&](const Pointer &Ptr) {
|
||||
if (Size == 0) {
|
||||
if constexpr (std::is_same_v<ResultT, APValue>)
|
||||
Result = APValue(APValue::UninitArray{}, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Ptr.isLive() || !Ptr.getFieldDesc()->isPrimitiveArray())
|
||||
return false;
|
||||
|
||||
// Must be char.
|
||||
if (Ptr.getFieldDesc()->getElemSize() != 1 /*bytes*/)
|
||||
return false;
|
||||
|
||||
if (Size > Ptr.getNumElems()) {
|
||||
Parent.FFDiag(SizeExpr, diag::note_constexpr_access_past_end) << AK_Read;
|
||||
Size = Ptr.getNumElems();
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<ResultT, APValue>) {
|
||||
QualType CharTy = PtrExpr->getType()->getPointeeType();
|
||||
Result = APValue(APValue::UninitArray{}, Size, Size);
|
||||
for (uint64_t I = 0; I != Size; ++I) {
|
||||
if (std::optional<APValue> ElemVal =
|
||||
Ptr.atIndex(I).toRValue(*this, CharTy))
|
||||
Result.getArrayInitializedElt(I) = *ElemVal;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
assert((std::is_same_v<ResultT, std::string>));
|
||||
if (Size < Result.max_size())
|
||||
Result.resize(Size);
|
||||
Result.assign(reinterpret_cast<const char *>(Ptr.getRawAddress()), Size);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (PtrRes.isInvalid()) {
|
||||
C.cleanup();
|
||||
Stk.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr,
|
||||
const Expr *PtrExpr, APValue &Result) {
|
||||
assert(SizeExpr);
|
||||
assert(PtrExpr);
|
||||
|
||||
return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result);
|
||||
}
|
||||
|
||||
bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr,
|
||||
const Expr *PtrExpr, std::string &Result) {
|
||||
assert(SizeExpr);
|
||||
assert(PtrExpr);
|
||||
|
||||
return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result);
|
||||
}
|
||||
|
||||
const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); }
|
||||
|
||||
std::optional<PrimType> Context::classify(QualType T) const {
|
||||
|
||||
@@ -59,6 +59,11 @@ public:
|
||||
/// Evaluates a toplevel initializer.
|
||||
bool evaluateAsInitializer(State &Parent, const VarDecl *VD, APValue &Result);
|
||||
|
||||
bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
|
||||
const Expr *PtrExpr, APValue &Result);
|
||||
bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
|
||||
const Expr *PtrExpr, std::string &Result);
|
||||
|
||||
/// Returns the AST context.
|
||||
ASTContext &getASTContext() const { return Ctx; }
|
||||
/// Returns the language options.
|
||||
@@ -120,6 +125,10 @@ private:
|
||||
/// Runs a function.
|
||||
bool Run(State &Parent, const Function *Func);
|
||||
|
||||
template <typename ResultT>
|
||||
bool evaluateStringRepr(State &Parent, const Expr *SizeExpr,
|
||||
const Expr *PtrExpr, ResultT &Result);
|
||||
|
||||
/// Current compilation context.
|
||||
ASTContext &Ctx;
|
||||
/// Interpreter stack, shared across invocations.
|
||||
|
||||
@@ -72,6 +72,24 @@ EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
|
||||
return std::move(this->EvalResult);
|
||||
}
|
||||
|
||||
EvaluationResult EvalEmitter::interpretAsPointer(const Expr *E,
|
||||
PtrCallback PtrCB) {
|
||||
|
||||
S.setEvalLocation(E->getExprLoc());
|
||||
this->ConvertResultToRValue = false;
|
||||
this->CheckFullyInitialized = false;
|
||||
this->PtrCB = PtrCB;
|
||||
EvalResult.setSource(E);
|
||||
|
||||
if (!this->visitExpr(E, /*DestroyToplevelScope=*/true)) {
|
||||
// EvalResult may already have a result set, but something failed
|
||||
// after that (e.g. evaluating destructors).
|
||||
EvalResult.setInvalid();
|
||||
}
|
||||
|
||||
return std::move(this->EvalResult);
|
||||
}
|
||||
|
||||
void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }
|
||||
|
||||
EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
|
||||
@@ -170,6 +188,10 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're returning a raw pointer, call our callback.
|
||||
if (this->PtrCB)
|
||||
return (*this->PtrCB)(Ptr);
|
||||
|
||||
if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
|
||||
return false;
|
||||
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
|
||||
|
||||
@@ -32,11 +32,14 @@ public:
|
||||
using LabelTy = uint32_t;
|
||||
using AddrTy = uintptr_t;
|
||||
using Local = Scope::Local;
|
||||
using PtrCallback = llvm::function_ref<bool(const Pointer &)>;
|
||||
|
||||
EvaluationResult interpretExpr(const Expr *E,
|
||||
bool ConvertResultToRValue = false,
|
||||
bool DestroyToplevelScope = false);
|
||||
EvaluationResult interpretDecl(const VarDecl *VD, bool CheckFullyInitialized);
|
||||
/// Interpret the given Expr to a Pointer.
|
||||
EvaluationResult interpretAsPointer(const Expr *E, PtrCallback PtrCB);
|
||||
|
||||
/// Clean up all resources.
|
||||
void cleanup();
|
||||
@@ -104,6 +107,8 @@ private:
|
||||
/// Whether we should check if the result has been fully
|
||||
/// initialized.
|
||||
bool CheckFullyInitialized = false;
|
||||
/// Callback to call when using interpretAsPointer.
|
||||
std::optional<PtrCallback> PtrCB;
|
||||
|
||||
/// Temporaries which require storage.
|
||||
llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Locals;
|
||||
|
||||
@@ -61,11 +61,6 @@ private:
|
||||
Value = std::move(V);
|
||||
Kind = RValue;
|
||||
}
|
||||
void setPointer(const Pointer P) {
|
||||
assert(empty());
|
||||
Value = P;
|
||||
Kind = LValue;
|
||||
}
|
||||
void setFunctionPointer(const FunctionPointer &P) {
|
||||
assert(empty());
|
||||
Value = P;
|
||||
@@ -88,6 +83,7 @@ public:
|
||||
bool isInvalid() const { return Kind == Invalid; }
|
||||
bool isLValue() const { return Kind == LValue; }
|
||||
bool isRValue() const { return Kind == RValue; }
|
||||
bool isPointer() const { return std::holds_alternative<Pointer>(Value); }
|
||||
|
||||
/// Returns an APValue for the evaluation result. The returned
|
||||
/// APValue might be an LValue or RValue.
|
||||
|
||||
@@ -613,6 +613,13 @@ public:
|
||||
|
||||
const Block *block() const { return asBlockPointer().Pointee; }
|
||||
|
||||
/// If backed by actual data (i.e. a block pointer), return
|
||||
/// an address to that data.
|
||||
const std::byte *getRawAddress() const {
|
||||
assert(isBlockPointer());
|
||||
return asBlockPointer().Pointee->rawData() + Offset;
|
||||
}
|
||||
|
||||
/// Returns the index into an array.
|
||||
int64_t getIndex() const {
|
||||
if (!isBlockPointer())
|
||||
|
||||
@@ -18019,10 +18019,14 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result,
|
||||
const Expr *PtrExpression,
|
||||
ASTContext &Ctx,
|
||||
Expr::EvalResult &Status) {
|
||||
LValue String;
|
||||
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
|
||||
Info.InConstantContext = true;
|
||||
|
||||
if (Info.EnableNewConstInterp)
|
||||
return Info.Ctx.getInterpContext().evaluateCharRange(Info, SizeExpression,
|
||||
PtrExpression, Result);
|
||||
|
||||
LValue String;
|
||||
FullExpressionRAII Scope(Info);
|
||||
APSInt SizeValue;
|
||||
if (!::EvaluateInteger(SizeExpression, SizeValue, Info))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -triple x86_64-gnu-linux
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -triple x86_64-gnu-linux -fexperimental-new-constant-interpreter
|
||||
|
||||
template <bool Leak>
|
||||
struct RAIIBase {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// RUN: %clang_cc1 -std=c++2c -triple=x86_64-linux -fsyntax-only %s -verify
|
||||
// RUN: %clang_cc1 -std=c++2c -triple=x86_64-linux -fsyntax-only %s -verify -fexperimental-new-constant-interpreter
|
||||
|
||||
static_assert(true, "");
|
||||
static_assert(true, 0); // expected-error {{the message in a static assertion must be a string literal or an object with 'data()' and 'size()' member functions}}
|
||||
|
||||
Reference in New Issue
Block a user