xref: /llvm-project/clang/unittests/AST/ByteCode/toAPValue.cpp (revision 2c82079924f1e43b211a6f233ea04ea911a7b581)
1 #include "../../../lib/AST/ByteCode/Context.h"
2 #include "../../../lib/AST/ByteCode/Descriptor.h"
3 #include "../../../lib/AST/ByteCode/Program.h"
4 #include "clang/AST/ASTContext.h"
5 #include "clang/AST/Decl.h"
6 #include "clang/ASTMatchers/ASTMatchFinder.h"
7 #include "clang/ASTMatchers/ASTMatchers.h"
8 #include "clang/Tooling/Tooling.h"
9 #include "gtest/gtest.h"
10 
11 using namespace clang;
12 using namespace clang::interp;
13 using namespace clang::ast_matchers;
14 
15 /// Test the various toAPValue implementations.
16 TEST(ToAPValue, Pointers) {
17   constexpr char Code[] =
18       "struct A { bool a; bool z; };\n"
19       "struct S {\n"
20       "  A a[3];\n"
21       "};\n"
22       "constexpr S d = {{{true, false}, {false, true}, {false, false}}};\n"
23       "constexpr const bool *b = &d.a[1].z;\n"
24       "const void *p = (void*)12;\n"
25       "const void *nullp = (void*)0;\n"
26       "extern int earr[5][5];\n"
27       "constexpr const int *arrp = &earr[2][4];\n";
28 
29   auto AST = tooling::buildASTFromCodeWithArgs(
30       Code, {"-fexperimental-new-constant-interpreter"});
31 
32   auto &ASTCtx = AST->getASTContext();
33   auto &Ctx = AST->getASTContext().getInterpContext();
34   Program &Prog = Ctx.getProgram();
35 
36   auto getDecl = [&](const char *Name) -> const ValueDecl * {
37     auto Nodes =
38         match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
39     assert(Nodes.size() == 1);
40     const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
41     assert(D);
42     return D;
43   };
44   auto getGlobalPtr = [&](const char *Name) -> Pointer {
45     const VarDecl *D = cast<VarDecl>(getDecl(Name));
46     return Prog.getPtrGlobal(*Prog.getGlobal(D));
47   };
48 
49   {
50     const Pointer &GP = getGlobalPtr("b");
51     const Pointer &P = GP.deref<Pointer>();
52     ASSERT_TRUE(P.isLive());
53     APValue A = P.toAPValue(ASTCtx);
54     ASSERT_TRUE(A.isLValue());
55     ASSERT_TRUE(A.hasLValuePath());
56     const auto &Path = A.getLValuePath();
57     ASSERT_EQ(Path.size(), 3u);
58     ASSERT_EQ(A.getLValueBase(), getDecl("d"));
59     // FIXME: Also test all path elements.
60   }
61 
62   {
63     const ValueDecl *D = getDecl("p");
64     ASSERT_NE(D, nullptr);
65     const Pointer &GP = getGlobalPtr("p");
66     const Pointer &P = GP.deref<Pointer>();
67     ASSERT_TRUE(P.isIntegralPointer());
68     APValue A = P.toAPValue(ASTCtx);
69     ASSERT_TRUE(A.isLValue());
70     ASSERT_TRUE(A.getLValueBase().isNull());
71     APSInt I;
72     bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
73     ASSERT_TRUE(Success);
74     ASSERT_EQ(I, 12);
75   }
76 
77   {
78     const ValueDecl *D = getDecl("nullp");
79     ASSERT_NE(D, nullptr);
80     const Pointer &GP = getGlobalPtr("nullp");
81     const Pointer &P = GP.deref<Pointer>();
82     ASSERT_TRUE(P.isIntegralPointer());
83     APValue A = P.toAPValue(ASTCtx);
84     ASSERT_TRUE(A.isLValue());
85     ASSERT_TRUE(A.getLValueBase().isNull());
86     ASSERT_TRUE(A.isNullPointer());
87     APSInt I;
88     bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
89     ASSERT_TRUE(Success);
90     ASSERT_EQ(I, 0);
91   }
92 
93   // A multidimensional array.
94   {
95     const ValueDecl *D = getDecl("arrp");
96     ASSERT_NE(D, nullptr);
97     const Pointer &GP = getGlobalPtr("arrp").deref<Pointer>();
98     APValue A = GP.toAPValue(ASTCtx);
99     ASSERT_TRUE(A.isLValue());
100     ASSERT_TRUE(A.hasLValuePath());
101     ASSERT_EQ(A.getLValuePath().size(), 2u);
102     ASSERT_EQ(A.getLValuePath()[0].getAsArrayIndex(), 2u);
103     ASSERT_EQ(A.getLValuePath()[1].getAsArrayIndex(), 4u);
104     ASSERT_EQ(A.getLValueOffset().getQuantity(), 56u);
105     ASSERT_TRUE(
106         GP.atIndex(0).getFieldDesc()->getElemQualType()->isIntegerType());
107   }
108 }
109 
110 TEST(ToAPValue, FunctionPointers) {
111   constexpr char Code[] = " constexpr bool foo() { return true; }\n"
112                           " constexpr bool (*func)() = foo;\n"
113                           " constexpr bool (*nullp)() = nullptr;\n";
114 
115   auto AST = tooling::buildASTFromCodeWithArgs(
116       Code, {"-fexperimental-new-constant-interpreter"});
117 
118   auto &ASTCtx = AST->getASTContext();
119   auto &Ctx = AST->getASTContext().getInterpContext();
120   Program &Prog = Ctx.getProgram();
121 
122   auto getDecl = [&](const char *Name) -> const ValueDecl * {
123     auto Nodes =
124         match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
125     assert(Nodes.size() == 1);
126     const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
127     assert(D);
128     return D;
129   };
130 
131   auto getGlobalPtr = [&](const char *Name) -> Pointer {
132     const VarDecl *D = cast<VarDecl>(getDecl(Name));
133     return Prog.getPtrGlobal(*Prog.getGlobal(D));
134   };
135 
136   {
137     const Pointer &GP = getGlobalPtr("func");
138     const FunctionPointer &FP = GP.deref<FunctionPointer>();
139     ASSERT_FALSE(FP.isZero());
140     APValue A = FP.toAPValue(ASTCtx);
141     ASSERT_TRUE(A.hasValue());
142     ASSERT_TRUE(A.isLValue());
143     ASSERT_TRUE(A.hasLValuePath());
144     const auto &Path = A.getLValuePath();
145     ASSERT_EQ(Path.size(), 0u);
146     ASSERT_FALSE(A.getLValueBase().isNull());
147     ASSERT_EQ(A.getLValueBase().dyn_cast<const ValueDecl *>(), getDecl("foo"));
148   }
149 
150   {
151     const ValueDecl *D = getDecl("nullp");
152     ASSERT_NE(D, nullptr);
153     const Pointer &GP = getGlobalPtr("nullp");
154     const auto &P = GP.deref<FunctionPointer>();
155     APValue A = P.toAPValue(ASTCtx);
156     ASSERT_TRUE(A.isLValue());
157     ASSERT_TRUE(A.getLValueBase().isNull());
158     ASSERT_TRUE(A.isNullPointer());
159     APSInt I;
160     bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
161     ASSERT_TRUE(Success);
162     ASSERT_EQ(I, 0);
163   }
164 }
165 
166 TEST(ToAPValue, FunctionPointersC) {
167   // NB: The declaration of func2 is useless, but it makes us register a global
168   // variable for func.
169   constexpr char Code[] = "const int (* const func)(int *) = (void*)17;\n"
170                           "const int (*func2)(int *) = func;\n";
171   auto AST = tooling::buildASTFromCodeWithArgs(
172       Code, {"-x", "c", "-fexperimental-new-constant-interpreter"});
173 
174   auto &ASTCtx = AST->getASTContext();
175   auto &Ctx = AST->getASTContext().getInterpContext();
176   Program &Prog = Ctx.getProgram();
177 
178   auto getDecl = [&](const char *Name) -> const ValueDecl * {
179     auto Nodes =
180         match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
181     assert(Nodes.size() == 1);
182     const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
183     assert(D);
184     return D;
185   };
186 
187   auto getGlobalPtr = [&](const char *Name) -> Pointer {
188     const VarDecl *D = cast<VarDecl>(getDecl(Name));
189     return Prog.getPtrGlobal(*Prog.getGlobal(D));
190   };
191 
192   {
193     const ValueDecl *D = getDecl("func");
194     const Pointer &GP = getGlobalPtr("func");
195     ASSERT_TRUE(GP.isLive());
196     const FunctionPointer &FP = GP.deref<FunctionPointer>();
197     ASSERT_FALSE(FP.isZero());
198     APValue A = FP.toAPValue(ASTCtx);
199     ASSERT_TRUE(A.hasValue());
200     ASSERT_TRUE(A.isLValue());
201     const auto &Path = A.getLValuePath();
202     ASSERT_EQ(Path.size(), 0u);
203     ASSERT_TRUE(A.getLValueBase().isNull());
204     APSInt I;
205     bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
206     ASSERT_TRUE(Success);
207     ASSERT_EQ(I, 17);
208   }
209 }
210 
211 TEST(ToAPValue, MemberPointers) {
212   constexpr char Code[] = "struct S {\n"
213                           "  int m, n;\n"
214                           "};\n"
215                           "constexpr int S::*pm = &S::m;\n"
216                           "constexpr int S::*nn = nullptr;\n";
217 
218   auto AST = tooling::buildASTFromCodeWithArgs(
219       Code, {"-fexperimental-new-constant-interpreter"});
220 
221   auto &ASTCtx = AST->getASTContext();
222   auto &Ctx = AST->getASTContext().getInterpContext();
223   Program &Prog = Ctx.getProgram();
224 
225   auto getDecl = [&](const char *Name) -> const ValueDecl * {
226     auto Nodes =
227         match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
228     assert(Nodes.size() == 1);
229     const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
230     assert(D);
231     return D;
232   };
233 
234   auto getGlobalPtr = [&](const char *Name) -> Pointer {
235     const VarDecl *D = cast<VarDecl>(getDecl(Name));
236     return Prog.getPtrGlobal(*Prog.getGlobal(D));
237   };
238 
239   {
240     const Pointer &GP = getGlobalPtr("pm");
241     ASSERT_TRUE(GP.isLive());
242     const MemberPointer &FP = GP.deref<MemberPointer>();
243     APValue A = FP.toAPValue(ASTCtx);
244     ASSERT_EQ(A.getMemberPointerDecl(), getDecl("m"));
245     ASSERT_EQ(A.getKind(), APValue::MemberPointer);
246   }
247 
248   {
249     const Pointer &GP = getGlobalPtr("nn");
250     ASSERT_TRUE(GP.isLive());
251     const MemberPointer &NP = GP.deref<MemberPointer>();
252     ASSERT_TRUE(NP.isZero());
253     APValue A = NP.toAPValue(ASTCtx);
254     ASSERT_EQ(A.getKind(), APValue::MemberPointer);
255   }
256 }
257