Files
clang-p2996/clang/lib/AST/Interp/EvaluationResult.cpp
Chris B 28ddbd4a86 [NFC] Refactor ConstantArrayType size storage (#85716)
In PR #79382, I need to add a new type that derives from
ConstantArrayType. This means that ConstantArrayType can no longer use
`llvm::TrailingObjects` to store the trailing optional Expr*.

This change refactors ConstantArrayType to store a 60-bit integer and
4-bits for the integer size in bytes. This replaces the APInt field
previously in the type but preserves enough information to recreate it
where needed.

To reduce the number of places where the APInt is re-constructed I've
also added some helper methods to the ConstantArrayType to allow some
common use cases that operate on either the stored small integer or the
APInt as appropriate.

Resolves #85124.
2024-03-26 14:15:56 -05:00

200 lines
6.1 KiB
C++

//===----- EvaluationResult.cpp - Result class for the VM ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "EvaluationResult.h"
#include "Context.h"
#include "InterpState.h"
#include "Record.h"
#include "clang/AST/ExprCXX.h"
namespace clang {
namespace interp {
APValue EvaluationResult::toAPValue() const {
assert(!empty());
switch (Kind) {
case LValue:
// Either a pointer or a function pointer.
if (const auto *P = std::get_if<Pointer>(&Value))
return P->toAPValue();
else if (const auto *FP = std::get_if<FunctionPointer>(&Value))
return FP->toAPValue();
else
llvm_unreachable("Unhandled LValue type");
break;
case RValue:
return std::get<APValue>(Value);
case Valid:
return APValue();
default:
llvm_unreachable("Unhandled result kind?");
}
}
std::optional<APValue> EvaluationResult::toRValue() const {
if (Kind == RValue)
return toAPValue();
assert(Kind == LValue);
// We have a pointer and want an RValue.
if (const auto *P = std::get_if<Pointer>(&Value))
return P->toRValue(*Ctx);
else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope
return FP->toAPValue();
llvm_unreachable("Unhandled lvalue kind");
}
static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc,
const FieldDecl *SubObjDecl) {
assert(SubObjDecl && "Subobject declaration does not exist");
S.FFDiag(Loc, diag::note_constexpr_uninitialized)
<< /*(name)*/ 1 << SubObjDecl;
S.Note(SubObjDecl->getLocation(),
diag::note_constexpr_subobject_declared_here);
}
static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
const Pointer &BasePtr, const Record *R);
static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc,
const Pointer &BasePtr,
const ConstantArrayType *CAT) {
bool Result = true;
size_t NumElems = CAT->getZExtSize();
QualType ElemType = CAT->getElementType();
if (ElemType->isRecordType()) {
const Record *R = BasePtr.getElemRecord();
for (size_t I = 0; I != NumElems; ++I) {
Pointer ElemPtr = BasePtr.atIndex(I).narrow();
Result &= CheckFieldsInitialized(S, Loc, ElemPtr, R);
}
} else if (const auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {
for (size_t I = 0; I != NumElems; ++I) {
Pointer ElemPtr = BasePtr.atIndex(I).narrow();
Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT);
}
} else {
for (size_t I = 0; I != NumElems; ++I) {
if (!BasePtr.atIndex(I).isInitialized()) {
DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField());
Result = false;
}
}
}
return Result;
}
static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
const Pointer &BasePtr, const Record *R) {
assert(R);
bool Result = true;
// Check all fields of this record are initialized.
for (const Record::Field &F : R->fields()) {
Pointer FieldPtr = BasePtr.atField(F.Offset);
QualType FieldType = F.Decl->getType();
if (FieldType->isRecordType()) {
Result &= CheckFieldsInitialized(S, Loc, FieldPtr, FieldPtr.getRecord());
} else if (FieldType->isIncompleteArrayType()) {
// Nothing to do here.
} else if (F.Decl->isUnnamedBitfield()) {
// Nothing do do here.
} else if (FieldType->isArrayType()) {
const auto *CAT =
cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
Result &= CheckArrayInitialized(S, Loc, FieldPtr, CAT);
} else if (!FieldPtr.isInitialized()) {
DiagnoseUninitializedSubobject(S, Loc, F.Decl);
Result = false;
}
}
// Check Fields in all bases
for (const Record::Base &B : R->bases()) {
Pointer P = BasePtr.atField(B.Offset);
if (!P.isInitialized()) {
S.FFDiag(BasePtr.getDeclDesc()->asDecl()->getLocation(),
diag::note_constexpr_uninitialized_base)
<< B.Desc->getType();
return false;
}
Result &= CheckFieldsInitialized(S, Loc, P, B.R);
}
// TODO: Virtual bases
return Result;
}
bool EvaluationResult::checkFullyInitialized(InterpState &S,
const Pointer &Ptr) const {
assert(Source);
assert(empty());
// Our Source must be a VarDecl.
const Decl *SourceDecl = Source.dyn_cast<const Decl *>();
assert(SourceDecl);
const auto *VD = cast<VarDecl>(SourceDecl);
assert(VD->getType()->isRecordType() || VD->getType()->isArrayType());
SourceLocation InitLoc = VD->getAnyInitializer()->getExprLoc();
assert(!Ptr.isZero());
if (const Record *R = Ptr.getRecord())
return CheckFieldsInitialized(S, InitLoc, Ptr, R);
const auto *CAT =
cast<ConstantArrayType>(Ptr.getType()->getAsArrayTypeUnsafe());
return CheckArrayInitialized(S, InitLoc, Ptr, CAT);
}
void EvaluationResult::dump() const {
assert(Ctx);
auto &OS = llvm::errs();
const ASTContext &ASTCtx = Ctx->getASTContext();
switch (Kind) {
case Empty:
OS << "Empty\n";
break;
case RValue:
OS << "RValue: ";
std::get<APValue>(Value).dump(OS, ASTCtx);
break;
case LValue: {
assert(Source);
QualType SourceType;
if (const auto *D = Source.dyn_cast<const Decl *>()) {
if (const auto *VD = dyn_cast<ValueDecl>(D))
SourceType = VD->getType();
} else if (const auto *E = Source.dyn_cast<const Expr *>()) {
SourceType = E->getType();
}
OS << "LValue: ";
if (const auto *P = std::get_if<Pointer>(&Value))
P->toAPValue().printPretty(OS, ASTCtx, SourceType);
else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope
FP->toAPValue().printPretty(OS, ASTCtx, SourceType);
OS << "\n";
break;
}
case Invalid:
OS << "Invalid\n";
break;
case Valid:
OS << "Valid\n";
break;
}
}
} // namespace interp
} // namespace clang