Skip to content

Commit ecc73a6

Browse files
authored
[clang][bytecode] Use bytecode interpreter in EvaluateCharRangeAsString (#138461)
This was always using the ast walker.
1 parent 003fa77 commit ecc73a6

9 files changed

+130
-6
lines changed

clang/lib/AST/ByteCode/Context.cpp

+79
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,85 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
134134
return true;
135135
}
136136

137+
template <typename ResultT>
138+
bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr,
139+
const Expr *PtrExpr, ResultT &Result) {
140+
assert(Stk.empty());
141+
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
142+
143+
// Evaluate size value.
144+
APValue SizeValue;
145+
if (!evaluateAsRValue(Parent, SizeExpr, SizeValue))
146+
return false;
147+
148+
if (!SizeValue.isInt())
149+
return false;
150+
uint64_t Size = SizeValue.getInt().getZExtValue();
151+
152+
auto PtrRes = C.interpretAsPointer(PtrExpr, [&](const Pointer &Ptr) {
153+
if (Size == 0) {
154+
if constexpr (std::is_same_v<ResultT, APValue>)
155+
Result = APValue(APValue::UninitArray{}, 0, 0);
156+
return true;
157+
}
158+
159+
if (!Ptr.isLive() || !Ptr.getFieldDesc()->isPrimitiveArray())
160+
return false;
161+
162+
// Must be char.
163+
if (Ptr.getFieldDesc()->getElemSize() != 1 /*bytes*/)
164+
return false;
165+
166+
if (Size > Ptr.getNumElems()) {
167+
Parent.FFDiag(SizeExpr, diag::note_constexpr_access_past_end) << AK_Read;
168+
Size = Ptr.getNumElems();
169+
}
170+
171+
if constexpr (std::is_same_v<ResultT, APValue>) {
172+
QualType CharTy = PtrExpr->getType()->getPointeeType();
173+
Result = APValue(APValue::UninitArray{}, Size, Size);
174+
for (uint64_t I = 0; I != Size; ++I) {
175+
if (std::optional<APValue> ElemVal =
176+
Ptr.atIndex(I).toRValue(*this, CharTy))
177+
Result.getArrayInitializedElt(I) = *ElemVal;
178+
else
179+
return false;
180+
}
181+
} else {
182+
assert((std::is_same_v<ResultT, std::string>));
183+
if (Size < Result.max_size())
184+
Result.resize(Size);
185+
Result.assign(reinterpret_cast<const char *>(Ptr.getRawAddress()), Size);
186+
}
187+
188+
return true;
189+
});
190+
191+
if (PtrRes.isInvalid()) {
192+
C.cleanup();
193+
Stk.clear();
194+
return false;
195+
}
196+
197+
return true;
198+
}
199+
200+
bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr,
201+
const Expr *PtrExpr, APValue &Result) {
202+
assert(SizeExpr);
203+
assert(PtrExpr);
204+
205+
return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result);
206+
}
207+
208+
bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr,
209+
const Expr *PtrExpr, std::string &Result) {
210+
assert(SizeExpr);
211+
assert(PtrExpr);
212+
213+
return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result);
214+
}
215+
137216
const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); }
138217

139218
std::optional<PrimType> Context::classify(QualType T) const {

clang/lib/AST/ByteCode/Context.h

+9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ class Context final {
5959
/// Evaluates a toplevel initializer.
6060
bool evaluateAsInitializer(State &Parent, const VarDecl *VD, APValue &Result);
6161

62+
bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
63+
const Expr *PtrExpr, APValue &Result);
64+
bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
65+
const Expr *PtrExpr, std::string &Result);
66+
6267
/// Returns the AST context.
6368
ASTContext &getASTContext() const { return Ctx; }
6469
/// Returns the language options.
@@ -120,6 +125,10 @@ class Context final {
120125
/// Runs a function.
121126
bool Run(State &Parent, const Function *Func);
122127

128+
template <typename ResultT>
129+
bool evaluateStringRepr(State &Parent, const Expr *SizeExpr,
130+
const Expr *PtrExpr, ResultT &Result);
131+
123132
/// Current compilation context.
124133
ASTContext &Ctx;
125134
/// Interpreter stack, shared across invocations.

clang/lib/AST/ByteCode/EvalEmitter.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,24 @@ EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
7272
return std::move(this->EvalResult);
7373
}
7474

75+
EvaluationResult EvalEmitter::interpretAsPointer(const Expr *E,
76+
PtrCallback PtrCB) {
77+
78+
S.setEvalLocation(E->getExprLoc());
79+
this->ConvertResultToRValue = false;
80+
this->CheckFullyInitialized = false;
81+
this->PtrCB = PtrCB;
82+
EvalResult.setSource(E);
83+
84+
if (!this->visitExpr(E, /*DestroyToplevelScope=*/true)) {
85+
// EvalResult may already have a result set, but something failed
86+
// after that (e.g. evaluating destructors).
87+
EvalResult.setInvalid();
88+
}
89+
90+
return std::move(this->EvalResult);
91+
}
92+
7593
void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }
7694

7795
EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
@@ -170,6 +188,10 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
170188
return true;
171189
}
172190

191+
// If we're returning a raw pointer, call our callback.
192+
if (this->PtrCB)
193+
return (*this->PtrCB)(Ptr);
194+
173195
if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
174196
return false;
175197
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))

clang/lib/AST/ByteCode/EvalEmitter.h

+5
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,14 @@ class EvalEmitter : public SourceMapper {
3232
using LabelTy = uint32_t;
3333
using AddrTy = uintptr_t;
3434
using Local = Scope::Local;
35+
using PtrCallback = llvm::function_ref<bool(const Pointer &)>;
3536

3637
EvaluationResult interpretExpr(const Expr *E,
3738
bool ConvertResultToRValue = false,
3839
bool DestroyToplevelScope = false);
3940
EvaluationResult interpretDecl(const VarDecl *VD, bool CheckFullyInitialized);
41+
/// Interpret the given Expr to a Pointer.
42+
EvaluationResult interpretAsPointer(const Expr *E, PtrCallback PtrCB);
4043

4144
/// Clean up all resources.
4245
void cleanup();
@@ -104,6 +107,8 @@ class EvalEmitter : public SourceMapper {
104107
/// Whether we should check if the result has been fully
105108
/// initialized.
106109
bool CheckFullyInitialized = false;
110+
/// Callback to call when using interpretAsPointer.
111+
std::optional<PtrCallback> PtrCB;
107112

108113
/// Temporaries which require storage.
109114
llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Locals;

clang/lib/AST/ByteCode/EvaluationResult.h

+1-5
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,6 @@ class EvaluationResult final {
6161
Value = std::move(V);
6262
Kind = RValue;
6363
}
64-
void setPointer(const Pointer P) {
65-
assert(empty());
66-
Value = P;
67-
Kind = LValue;
68-
}
6964
void setFunctionPointer(const FunctionPointer &P) {
7065
assert(empty());
7166
Value = P;
@@ -88,6 +83,7 @@ class EvaluationResult final {
8883
bool isInvalid() const { return Kind == Invalid; }
8984
bool isLValue() const { return Kind == LValue; }
9085
bool isRValue() const { return Kind == RValue; }
86+
bool isPointer() const { return std::holds_alternative<Pointer>(Value); }
9187

9288
/// Returns an APValue for the evaluation result. The returned
9389
/// APValue might be an LValue or RValue.

clang/lib/AST/ByteCode/Pointer.h

+7
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,13 @@ class Pointer {
613613

614614
const Block *block() const { return asBlockPointer().Pointee; }
615615

616+
/// If backed by actual data (i.e. a block pointer), return
617+
/// an address to that data.
618+
const std::byte *getRawAddress() const {
619+
assert(isBlockPointer());
620+
return asBlockPointer().Pointee->rawData() + Offset;
621+
}
622+
616623
/// Returns the index into an array.
617624
int64_t getIndex() const {
618625
if (!isBlockPointer())

clang/lib/AST/ExprConstant.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -18019,10 +18019,14 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result,
1801918019
const Expr *PtrExpression,
1802018020
ASTContext &Ctx,
1802118021
Expr::EvalResult &Status) {
18022-
LValue String;
1802318022
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
1802418023
Info.InConstantContext = true;
1802518024

18025+
if (Info.EnableNewConstInterp)
18026+
return Info.Ctx.getInterpContext().evaluateCharRange(Info, SizeExpression,
18027+
PtrExpression, Result);
18028+
18029+
LValue String;
1802618030
FullExpressionRAII Scope(Info);
1802718031
APSInt SizeValue;
1802818032
if (!::EvaluateInteger(SizeExpression, SizeValue, Info))

clang/test/SemaCXX/gnu-asm-constexpr.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -triple x86_64-gnu-linux
2+
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -triple x86_64-gnu-linux -fexperimental-new-constant-interpreter
23

34
template <bool Leak>
45
struct RAIIBase {

clang/test/SemaCXX/static-assert-cxx26.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -std=c++2c -triple=x86_64-linux -fsyntax-only %s -verify
2+
// RUN: %clang_cc1 -std=c++2c -triple=x86_64-linux -fsyntax-only %s -verify -fexperimental-new-constant-interpreter
23

34
static_assert(true, "");
45
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}}

0 commit comments

Comments
 (0)