CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
408 lines
13 KiB
C++
408 lines
13 KiB
C++
#include "../../../lib/AST/ByteCode/Context.h"
|
|
#include "../../../lib/AST/ByteCode/Descriptor.h"
|
|
#include "../../../lib/AST/ByteCode/Program.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
|
#include "clang/Tooling/Tooling.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace clang;
|
|
using namespace clang::interp;
|
|
using namespace clang::ast_matchers;
|
|
|
|
/// Test the various toAPValue implementations.
|
|
TEST(ToAPValue, Pointers) {
|
|
constexpr char Code[] =
|
|
"struct A { bool a; bool z; };\n"
|
|
"struct S {\n"
|
|
" A a[3];\n"
|
|
"};\n"
|
|
"constexpr S d = {{{true, false}, {false, true}, {false, false}}};\n"
|
|
"constexpr const bool *b = &d.a[1].z;\n"
|
|
"const void *p = (void*)12;\n"
|
|
"const void *nullp = (void*)0;\n"
|
|
"extern int earr[5][5];\n"
|
|
"constexpr const int *arrp = &earr[2][4];\n";
|
|
|
|
auto AST = tooling::buildASTFromCodeWithArgs(
|
|
Code, {"-fexperimental-new-constant-interpreter"});
|
|
|
|
auto &ASTCtx = AST->getASTContext();
|
|
auto &Ctx = AST->getASTContext().getInterpContext();
|
|
Program &Prog = Ctx.getProgram();
|
|
|
|
auto getDecl = [&](const char *Name) -> const ValueDecl * {
|
|
auto Nodes =
|
|
match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
|
|
assert(Nodes.size() == 1);
|
|
const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
|
|
assert(D);
|
|
return D;
|
|
};
|
|
auto getGlobalPtr = [&](const char *Name) -> Pointer {
|
|
const VarDecl *D = cast<VarDecl>(getDecl(Name));
|
|
return Prog.getPtrGlobal(*Prog.getGlobal(D));
|
|
};
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("b");
|
|
const Pointer &P = GP.deref<Pointer>();
|
|
ASSERT_TRUE(P.isLive());
|
|
APValue A = P.toAPValue(ASTCtx);
|
|
ASSERT_TRUE(A.isLValue());
|
|
ASSERT_TRUE(A.hasLValuePath());
|
|
const auto &Path = A.getLValuePath();
|
|
ASSERT_EQ(Path.size(), 3u);
|
|
ASSERT_EQ(A.getLValueBase(), getDecl("d"));
|
|
// FIXME: Also test all path elements.
|
|
}
|
|
|
|
{
|
|
const ValueDecl *D = getDecl("p");
|
|
ASSERT_NE(D, nullptr);
|
|
const Pointer &GP = getGlobalPtr("p");
|
|
const Pointer &P = GP.deref<Pointer>();
|
|
ASSERT_TRUE(P.isIntegralPointer());
|
|
APValue A = P.toAPValue(ASTCtx);
|
|
ASSERT_TRUE(A.isLValue());
|
|
ASSERT_TRUE(A.getLValueBase().isNull());
|
|
APSInt I;
|
|
bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
|
|
ASSERT_TRUE(Success);
|
|
ASSERT_EQ(I, 12);
|
|
}
|
|
|
|
{
|
|
const ValueDecl *D = getDecl("nullp");
|
|
ASSERT_NE(D, nullptr);
|
|
const Pointer &GP = getGlobalPtr("nullp");
|
|
const Pointer &P = GP.deref<Pointer>();
|
|
ASSERT_TRUE(P.isIntegralPointer());
|
|
APValue A = P.toAPValue(ASTCtx);
|
|
ASSERT_TRUE(A.isLValue());
|
|
ASSERT_TRUE(A.getLValueBase().isNull());
|
|
ASSERT_TRUE(A.isNullPointer());
|
|
APSInt I;
|
|
bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
|
|
ASSERT_TRUE(Success);
|
|
ASSERT_EQ(I, 0);
|
|
}
|
|
|
|
// A multidimensional array.
|
|
{
|
|
const ValueDecl *D = getDecl("arrp");
|
|
ASSERT_NE(D, nullptr);
|
|
const Pointer &GP = getGlobalPtr("arrp").deref<Pointer>();
|
|
APValue A = GP.toAPValue(ASTCtx);
|
|
ASSERT_TRUE(A.isLValue());
|
|
ASSERT_TRUE(A.hasLValuePath());
|
|
ASSERT_EQ(A.getLValuePath().size(), 2u);
|
|
ASSERT_EQ(A.getLValuePath()[0].getAsArrayIndex(), 2u);
|
|
ASSERT_EQ(A.getLValuePath()[1].getAsArrayIndex(), 4u);
|
|
ASSERT_EQ(A.getLValueOffset().getQuantity(), 56u);
|
|
ASSERT_TRUE(
|
|
GP.atIndex(0).getFieldDesc()->getElemQualType()->isIntegerType());
|
|
}
|
|
}
|
|
|
|
TEST(ToAPValue, FunctionPointers) {
|
|
constexpr char Code[] = " constexpr bool foo() { return true; }\n"
|
|
" constexpr bool (*func)() = foo;\n"
|
|
" constexpr bool (*nullp)() = nullptr;\n";
|
|
|
|
auto AST = tooling::buildASTFromCodeWithArgs(
|
|
Code, {"-fexperimental-new-constant-interpreter"});
|
|
|
|
auto &ASTCtx = AST->getASTContext();
|
|
auto &Ctx = AST->getASTContext().getInterpContext();
|
|
Program &Prog = Ctx.getProgram();
|
|
|
|
auto getDecl = [&](const char *Name) -> const ValueDecl * {
|
|
auto Nodes =
|
|
match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
|
|
assert(Nodes.size() == 1);
|
|
const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
|
|
assert(D);
|
|
return D;
|
|
};
|
|
|
|
auto getGlobalPtr = [&](const char *Name) -> Pointer {
|
|
const VarDecl *D = cast<VarDecl>(getDecl(Name));
|
|
return Prog.getPtrGlobal(*Prog.getGlobal(D));
|
|
};
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("func");
|
|
const Pointer &FP = GP.deref<Pointer>();
|
|
ASSERT_FALSE(FP.isZero());
|
|
APValue A = FP.toAPValue(ASTCtx);
|
|
ASSERT_TRUE(A.hasValue());
|
|
ASSERT_TRUE(A.isLValue());
|
|
ASSERT_TRUE(A.hasLValuePath());
|
|
const auto &Path = A.getLValuePath();
|
|
ASSERT_EQ(Path.size(), 0u);
|
|
ASSERT_FALSE(A.getLValueBase().isNull());
|
|
ASSERT_EQ(A.getLValueBase().dyn_cast<const ValueDecl *>(), getDecl("foo"));
|
|
}
|
|
|
|
{
|
|
const ValueDecl *D = getDecl("nullp");
|
|
ASSERT_NE(D, nullptr);
|
|
const Pointer &GP = getGlobalPtr("nullp");
|
|
const auto &P = GP.deref<Pointer>();
|
|
ASSERT_TRUE(P.isZero());
|
|
APValue A = P.toAPValue(ASTCtx);
|
|
ASSERT_TRUE(A.isLValue());
|
|
ASSERT_TRUE(A.getLValueBase().isNull());
|
|
ASSERT_TRUE(A.isNullPointer());
|
|
APSInt I;
|
|
bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
|
|
ASSERT_TRUE(Success);
|
|
ASSERT_EQ(I, 0);
|
|
}
|
|
}
|
|
|
|
TEST(ToAPValue, FunctionPointersC) {
|
|
// NB: The declaration of func2 is useless, but it makes us register a global
|
|
// variable for func.
|
|
constexpr char Code[] = "const int (* const func)(int *) = (void*)17;\n"
|
|
"const int (*func2)(int *) = func;\n";
|
|
auto AST = tooling::buildASTFromCodeWithArgs(
|
|
Code, {"-x", "c", "-fexperimental-new-constant-interpreter"});
|
|
|
|
auto &ASTCtx = AST->getASTContext();
|
|
auto &Ctx = AST->getASTContext().getInterpContext();
|
|
Program &Prog = Ctx.getProgram();
|
|
|
|
auto getDecl = [&](const char *Name) -> const ValueDecl * {
|
|
auto Nodes =
|
|
match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
|
|
assert(Nodes.size() == 1);
|
|
const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
|
|
assert(D);
|
|
return D;
|
|
};
|
|
|
|
auto getGlobalPtr = [&](const char *Name) -> Pointer {
|
|
const VarDecl *D = cast<VarDecl>(getDecl(Name));
|
|
return Prog.getPtrGlobal(*Prog.getGlobal(D));
|
|
};
|
|
|
|
{
|
|
const ValueDecl *D = getDecl("func");
|
|
const Pointer &GP = getGlobalPtr("func");
|
|
ASSERT_TRUE(GP.isLive());
|
|
const Pointer &FP = GP.deref<Pointer>();
|
|
ASSERT_FALSE(FP.isZero());
|
|
APValue A = FP.toAPValue(ASTCtx);
|
|
ASSERT_TRUE(A.hasValue());
|
|
ASSERT_TRUE(A.isLValue());
|
|
const auto &Path = A.getLValuePath();
|
|
ASSERT_EQ(Path.size(), 0u);
|
|
ASSERT_TRUE(A.getLValueBase().isNull());
|
|
APSInt I;
|
|
bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
|
|
ASSERT_TRUE(Success);
|
|
ASSERT_EQ(I, 17);
|
|
}
|
|
}
|
|
|
|
TEST(ToAPValue, MemberPointers) {
|
|
constexpr char Code[] =
|
|
"struct S {\n"
|
|
" int m, n;\n"
|
|
"};\n"
|
|
"constexpr int S::*pm = &S::m;\n"
|
|
"constexpr int S::*nn = nullptr;\n"
|
|
|
|
"struct B{int x;};\n"
|
|
"struct C : B {int z; };\n"
|
|
"constexpr auto c1 = (int C::*)&B::x;\n"
|
|
"constexpr auto D = (int B::*)c1;\n"
|
|
|
|
"template<int n> struct T : T<n-1> { const int X = n;};\n"
|
|
"template<> struct T<0> { int nn_; char kk;};\n"
|
|
"template<> struct T<30> : T<29> { int mm; };\n"
|
|
"constexpr auto t1 = (int(T<10>::*))&T<30>::mm;\n"
|
|
"constexpr auto t2 = (int(T<11>::*))t1;\n"
|
|
"constexpr auto t3 = (int(T<20>::*))&T<30>::mm;\n"
|
|
"constexpr int (T<10>::*t4) = &T<0>::nn_;\n";
|
|
|
|
auto AST = tooling::buildASTFromCodeWithArgs(
|
|
Code, {"-fexperimental-new-constant-interpreter"});
|
|
|
|
auto &ASTCtx = AST->getASTContext();
|
|
auto &Ctx = AST->getASTContext().getInterpContext();
|
|
Program &Prog = Ctx.getProgram();
|
|
|
|
auto getDecl = [&](const char *Name) -> const ValueDecl * {
|
|
auto Nodes =
|
|
match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
|
|
assert(Nodes.size() == 1);
|
|
const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
|
|
assert(D);
|
|
return D;
|
|
};
|
|
|
|
auto getGlobalPtr = [&](const char *Name) -> Pointer {
|
|
const VarDecl *D = cast<VarDecl>(getDecl(Name));
|
|
return Prog.getPtrGlobal(*Prog.getGlobal(D));
|
|
};
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("pm");
|
|
ASSERT_TRUE(GP.isLive());
|
|
const MemberPointer &FP = GP.deref<MemberPointer>();
|
|
APValue A = FP.toAPValue(ASTCtx);
|
|
ASSERT_EQ(A.getMemberPointerDecl(), getDecl("m"));
|
|
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
|
|
ASSERT_EQ(A.getMemberPointerPath().size(), 0u);
|
|
ASSERT_FALSE(A.isMemberPointerToDerivedMember());
|
|
}
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("nn");
|
|
ASSERT_TRUE(GP.isLive());
|
|
const MemberPointer &NP = GP.deref<MemberPointer>();
|
|
ASSERT_TRUE(NP.isZero());
|
|
APValue A = NP.toAPValue(ASTCtx);
|
|
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
|
|
ASSERT_EQ(A.getMemberPointerPath().size(), 0u);
|
|
ASSERT_FALSE(A.isMemberPointerToDerivedMember());
|
|
}
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("c1");
|
|
ASSERT_TRUE(GP.isLive());
|
|
const MemberPointer &MP = GP.deref<MemberPointer>();
|
|
ASSERT_FALSE(MP.isZero());
|
|
APValue A = MP.toAPValue(ASTCtx);
|
|
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
|
|
ASSERT_EQ(A.getMemberPointerPath().size(), 1u);
|
|
ASSERT_FALSE(A.isMemberPointerToDerivedMember());
|
|
}
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("D");
|
|
ASSERT_TRUE(GP.isLive());
|
|
const MemberPointer &MP = GP.deref<MemberPointer>();
|
|
ASSERT_FALSE(MP.isZero());
|
|
APValue A = MP.toAPValue(ASTCtx);
|
|
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
|
|
ASSERT_EQ(A.getMemberPointerPath().size(), 0u);
|
|
ASSERT_FALSE(A.isMemberPointerToDerivedMember());
|
|
}
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("t1");
|
|
ASSERT_TRUE(GP.isLive());
|
|
const MemberPointer &MP = GP.deref<MemberPointer>();
|
|
ASSERT_FALSE(MP.isZero());
|
|
APValue A = MP.toAPValue(ASTCtx);
|
|
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
|
|
ASSERT_EQ(A.getMemberPointerPath().size(), 20u);
|
|
ASSERT_TRUE(A.isMemberPointerToDerivedMember());
|
|
}
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("t2");
|
|
ASSERT_TRUE(GP.isLive());
|
|
const MemberPointer &MP = GP.deref<MemberPointer>();
|
|
ASSERT_FALSE(MP.isZero());
|
|
APValue A = MP.toAPValue(ASTCtx);
|
|
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
|
|
ASSERT_EQ(A.getMemberPointerPath().size(), 19u);
|
|
ASSERT_TRUE(A.isMemberPointerToDerivedMember());
|
|
}
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("t3");
|
|
ASSERT_TRUE(GP.isLive());
|
|
const MemberPointer &MP = GP.deref<MemberPointer>();
|
|
ASSERT_FALSE(MP.isZero());
|
|
APValue A = MP.toAPValue(ASTCtx);
|
|
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
|
|
ASSERT_EQ(A.getMemberPointerPath().size(), 10u);
|
|
ASSERT_TRUE(A.isMemberPointerToDerivedMember());
|
|
}
|
|
|
|
{
|
|
const Pointer &GP = getGlobalPtr("t4");
|
|
ASSERT_TRUE(GP.isLive());
|
|
const MemberPointer &MP = GP.deref<MemberPointer>();
|
|
ASSERT_FALSE(MP.isZero());
|
|
APValue A = MP.toAPValue(ASTCtx);
|
|
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
|
|
ASSERT_EQ(A.getMemberPointerPath().size(), 10u);
|
|
ASSERT_FALSE(A.isMemberPointerToDerivedMember());
|
|
}
|
|
}
|
|
|
|
/// Compare outputs between the two interpreters.
|
|
TEST(ToAPValue, Comparison) {
|
|
constexpr char Code[] =
|
|
"constexpr int GI = 12;\n"
|
|
"constexpr const int *PI = &GI;\n"
|
|
"struct S{int a; };\n"
|
|
"constexpr S GS[] = {{}, {}};\n"
|
|
"constexpr const S* OS = GS + 1;\n"
|
|
"constexpr S DS = *OS;\n"
|
|
"constexpr int IA[2][2][2] = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}};\n"
|
|
"constexpr const int *PIA = IA[1][1];\n";
|
|
|
|
for (const char *Arg : {"", "-fexperimental-new-constant-interpreter"}) {
|
|
auto AST = tooling::buildASTFromCodeWithArgs(Code, {Arg});
|
|
|
|
auto getDecl = [&](const char *Name) -> const VarDecl * {
|
|
auto Nodes =
|
|
match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
|
|
assert(Nodes.size() == 1);
|
|
const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
|
|
assert(D);
|
|
return cast<VarDecl>(D);
|
|
};
|
|
|
|
{
|
|
const VarDecl *GIDecl = getDecl("GI");
|
|
const APValue *V = GIDecl->evaluateValue();
|
|
ASSERT_TRUE(V->isInt());
|
|
}
|
|
|
|
{
|
|
const VarDecl *GIDecl = getDecl("GI");
|
|
const VarDecl *PIDecl = getDecl("PI");
|
|
const APValue *V = PIDecl->evaluateValue();
|
|
ASSERT_TRUE(V->isLValue());
|
|
ASSERT_TRUE(V->hasLValuePath());
|
|
ASSERT_EQ(V->getLValueBase().get<const ValueDecl *>(), GIDecl);
|
|
ASSERT_EQ(V->getLValuePath().size(), 0u);
|
|
}
|
|
|
|
{
|
|
const APValue *V = getDecl("OS")->evaluateValue();
|
|
ASSERT_TRUE(V->isLValue());
|
|
ASSERT_EQ(V->getLValueOffset().getQuantity(), (unsigned)sizeof(int));
|
|
ASSERT_TRUE(V->hasLValuePath());
|
|
ASSERT_EQ(V->getLValuePath().size(), 1u);
|
|
ASSERT_EQ(V->getLValuePath()[0].getAsArrayIndex(), 1u);
|
|
}
|
|
|
|
{
|
|
const APValue *V = getDecl("DS")->evaluateValue();
|
|
ASSERT_TRUE(V->isStruct());
|
|
}
|
|
{
|
|
const APValue *V = getDecl("PIA")->evaluateValue();
|
|
ASSERT_TRUE(V->isLValue());
|
|
ASSERT_TRUE(V->hasLValuePath());
|
|
ASSERT_EQ(V->getLValuePath().size(), 3u);
|
|
ASSERT_EQ(V->getLValuePath()[0].getAsArrayIndex(), 1u);
|
|
ASSERT_EQ(V->getLValuePath()[1].getAsArrayIndex(), 1u);
|
|
ASSERT_EQ(V->getLValuePath()[2].getAsArrayIndex(), 0u);
|
|
ASSERT_EQ(V->getLValueOffset().getQuantity(), (unsigned)sizeof(int) * 6);
|
|
}
|
|
}
|
|
}
|