[clang][Interp] Do r-to-l conversion immediately when returning (#80662)

First, we need to register local constant variables in C, so we get the
same diagnostic behavior as the current interpeter.

Second, when returning an LValue (as a Pointer), which we eventually
convert to an RValue, we need to do the conversion immediately when
saving the Pointer in the EvaluationResult. Otherwise, we will possibly
deallocate the data before doing the conversion (which will look at the
Block*).
This commit is contained in:
Timm Baeder 2024-02-15 15:13:12 +01:00 committed by GitHub
parent c609211d91
commit d68aa303fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 42 additions and 18 deletions

View File

@ -3243,8 +3243,7 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
// This happens in C.
if (!Ctx.getLangOpts().CPlusPlus) {
if (const auto *VD = dyn_cast<VarDecl>(D);
VD && VD->hasGlobalStorage() && VD->getAnyInitializer() &&
VD->getType().isConstQualified()) {
VD && VD->getAnyInitializer() && VD->getType().isConstQualified()) {
if (!this->visitVarDecl(VD))
return false;
// Retry.

View File

@ -44,7 +44,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
auto Res = C.interpretExpr(E);
auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue());
if (Res.isInvalid()) {
Stk.clear();
@ -58,16 +58,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
Stk.clear();
#endif
// Implicit lvalue-to-rvalue conversion.
if (E->isGLValue()) {
std::optional<APValue> RValueResult = Res.toRValue();
if (!RValueResult) {
return false;
}
Result = *RValueResult;
} else {
Result = Res.toAPValue();
}
Result = Res.toAPValue();
return true;
}
@ -120,7 +111,8 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
!Res.checkFullyInitialized(C.getState()))
return false;
// lvalue-to-rvalue conversion.
// lvalue-to-rvalue conversion. We do this manually here so we can
// examine the result above before converting and returning it.
std::optional<APValue> RValueResult = Res.toRValue();
if (!RValueResult)
return false;

View File

@ -33,7 +33,9 @@ EvalEmitter::~EvalEmitter() {
}
}
EvaluationResult EvalEmitter::interpretExpr(const Expr *E) {
EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
bool ConvertResultToRValue) {
this->ConvertResultToRValue = ConvertResultToRValue;
EvalResult.setSource(E);
if (!this->visitExpr(E) && EvalResult.empty())
@ -119,12 +121,26 @@ template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
if (!isActive())
return true;
EvalResult.setPointer(S.Stk.pop<Pointer>());
const Pointer &Ptr = S.Stk.pop<Pointer>();
// Implicitly convert lvalue to rvalue, if requested.
if (ConvertResultToRValue) {
if (std::optional<APValue> V = Ptr.toRValue(Ctx)) {
EvalResult.setValue(*V);
} else {
return false;
}
} else {
EvalResult.setPointer(Ptr);
}
return true;
}
template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
if (!isActive())
return true;
// Function pointers cannot be converted to rvalues.
assert(!ConvertResultToRValue);
EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
return true;
}

View File

@ -34,7 +34,8 @@ public:
using AddrTy = uintptr_t;
using Local = Scope::Local;
EvaluationResult interpretExpr(const Expr *E);
EvaluationResult interpretExpr(const Expr *E,
bool ConvertResultToRValue = false);
EvaluationResult interpretDecl(const VarDecl *VD);
InterpState &getState() { return S; }
@ -86,6 +87,8 @@ private:
InterpState S;
/// Location to write the result to.
EvaluationResult EvalResult;
/// Whether the result should be converted to an RValue.
bool ConvertResultToRValue = false;
/// Temporaries which require storage.
llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Locals;

View File

@ -56,8 +56,8 @@ private:
void setSource(DeclTy D) { Source = D; }
void setValue(const APValue &V) {
// V could still be an LValue.
assert(empty());
assert(!V.isLValue());
Value = std::move(V);
Kind = RValue;
}

View File

@ -125,3 +125,16 @@ struct XY { int before; struct XX xx, *xp; float* after; } xy[] = {
0, // all-warning {{initializer overrides prior initialization of this subobject}}
&xy[2].xx.a, &xy[2].xx, &global_float
};
void t14(void) {
int array[256] = { 0 }; // expected-note {{array 'array' declared here}} \
// pedantic-expected-note {{array 'array' declared here}} \
// ref-note {{array 'array' declared here}} \
// pedantic-ref-note {{array 'array' declared here}}
const char b = -1;
int val = array[b]; // expected-warning {{array index -1 is before the beginning of the array}} \
// pedantic-expected-warning {{array index -1 is before the beginning of the array}} \
// ref-warning {{array index -1 is before the beginning of the array}} \
// pedantic-ref-warning {{array index -1 is before the beginning of the array}}
}

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -Wchar-subscripts -fsyntax-only -verify %s
// RUN: %clang_cc1 -Wchar-subscripts -fsyntax-only -verify %s -fexperimental-new-constant-interpreter
void t1(void) {
int array[1] = { 0 };