1 | #include "../../../lib/AST/Interp/Context.h" |
2 | #include "../../../lib/AST/Interp/Descriptor.h" |
3 | #include "../../../lib/AST/Interp/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 | |
27 | auto AST = tooling::buildASTFromCodeWithArgs( |
28 | Code, Args: {"-fexperimental-new-constant-interpreter" }); |
29 | |
30 | auto &Ctx = AST->getASTContext().getInterpContext(); |
31 | Program &Prog = Ctx.getProgram(); |
32 | |
33 | auto getDecl = [&](const char *Name) -> const ValueDecl * { |
34 | auto Nodes = |
35 | match(Matcher: valueDecl(hasName(Name)).bind(ID: "var" ), Context&: AST->getASTContext()); |
36 | assert(Nodes.size() == 1); |
37 | const auto *D = Nodes[0].getNodeAs<ValueDecl>(ID: "var" ); |
38 | assert(D); |
39 | return D; |
40 | }; |
41 | auto getGlobalPtr = [&](const char *Name) -> Pointer { |
42 | const VarDecl *D = cast<VarDecl>(Val: getDecl(Name)); |
43 | return Prog.getPtrGlobal(Idx: *Prog.getGlobal(D)); |
44 | }; |
45 | |
46 | { |
47 | const Pointer &GP = getGlobalPtr("b" ); |
48 | const Pointer &P = GP.deref<Pointer>(); |
49 | ASSERT_TRUE(P.isLive()); |
50 | APValue A = P.toAPValue(); |
51 | ASSERT_TRUE(A.isLValue()); |
52 | ASSERT_TRUE(A.hasLValuePath()); |
53 | const auto &Path = A.getLValuePath(); |
54 | ASSERT_EQ(Path.size(), 3u); |
55 | ASSERT_EQ(A.getLValueBase(), getDecl("d" )); |
56 | // FIXME: Also test all path elements. |
57 | } |
58 | |
59 | { |
60 | const ValueDecl *D = getDecl("p" ); |
61 | ASSERT_NE(D, nullptr); |
62 | const Pointer &GP = getGlobalPtr("p" ); |
63 | const Pointer &P = GP.deref<Pointer>(); |
64 | ASSERT_TRUE(P.isIntegralPointer()); |
65 | APValue A = P.toAPValue(); |
66 | ASSERT_TRUE(A.isLValue()); |
67 | ASSERT_TRUE(A.getLValueBase().isNull()); |
68 | APSInt I; |
69 | bool Success = A.toIntegralConstant(Result&: I, SrcTy: D->getType(), Ctx: AST->getASTContext()); |
70 | ASSERT_TRUE(Success); |
71 | ASSERT_EQ(I, 12); |
72 | } |
73 | |
74 | { |
75 | const ValueDecl *D = getDecl("nullp" ); |
76 | ASSERT_NE(D, nullptr); |
77 | const Pointer &GP = getGlobalPtr("nullp" ); |
78 | const Pointer &P = GP.deref<Pointer>(); |
79 | ASSERT_TRUE(P.isIntegralPointer()); |
80 | APValue A = P.toAPValue(); |
81 | ASSERT_TRUE(A.isLValue()); |
82 | ASSERT_TRUE(A.getLValueBase().isNull()); |
83 | ASSERT_TRUE(A.isNullPointer()); |
84 | APSInt I; |
85 | bool Success = A.toIntegralConstant(Result&: I, SrcTy: D->getType(), Ctx: AST->getASTContext()); |
86 | ASSERT_TRUE(Success); |
87 | ASSERT_EQ(I, 0); |
88 | } |
89 | } |
90 | |
91 | TEST(ToAPValue, FunctionPointers) { |
92 | constexpr char Code[] = " constexpr bool foo() { return true; }\n" |
93 | " constexpr bool (*func)() = foo;\n" |
94 | " constexpr bool (*nullp)() = nullptr;\n" ; |
95 | |
96 | auto AST = tooling::buildASTFromCodeWithArgs( |
97 | Code, Args: {"-fexperimental-new-constant-interpreter" }); |
98 | |
99 | auto &Ctx = AST->getASTContext().getInterpContext(); |
100 | Program &Prog = Ctx.getProgram(); |
101 | |
102 | auto getDecl = [&](const char *Name) -> const ValueDecl * { |
103 | auto Nodes = |
104 | match(Matcher: valueDecl(hasName(Name)).bind(ID: "var" ), Context&: AST->getASTContext()); |
105 | assert(Nodes.size() == 1); |
106 | const auto *D = Nodes[0].getNodeAs<ValueDecl>(ID: "var" ); |
107 | assert(D); |
108 | return D; |
109 | }; |
110 | |
111 | auto getGlobalPtr = [&](const char *Name) -> Pointer { |
112 | const VarDecl *D = cast<VarDecl>(Val: getDecl(Name)); |
113 | return Prog.getPtrGlobal(Idx: *Prog.getGlobal(D)); |
114 | }; |
115 | |
116 | { |
117 | const Pointer &GP = getGlobalPtr("func" ); |
118 | const FunctionPointer &FP = GP.deref<FunctionPointer>(); |
119 | ASSERT_FALSE(FP.isZero()); |
120 | APValue A = FP.toAPValue(); |
121 | ASSERT_TRUE(A.hasValue()); |
122 | ASSERT_TRUE(A.isLValue()); |
123 | ASSERT_TRUE(A.hasLValuePath()); |
124 | const auto &Path = A.getLValuePath(); |
125 | ASSERT_EQ(Path.size(), 0u); |
126 | ASSERT_FALSE(A.getLValueBase().isNull()); |
127 | ASSERT_EQ(A.getLValueBase().dyn_cast<const ValueDecl *>(), getDecl("foo" )); |
128 | } |
129 | |
130 | { |
131 | const ValueDecl *D = getDecl("nullp" ); |
132 | ASSERT_NE(D, nullptr); |
133 | const Pointer &GP = getGlobalPtr("nullp" ); |
134 | const auto &P = GP.deref<FunctionPointer>(); |
135 | APValue A = P.toAPValue(); |
136 | ASSERT_TRUE(A.isLValue()); |
137 | ASSERT_TRUE(A.getLValueBase().isNull()); |
138 | ASSERT_TRUE(A.isNullPointer()); |
139 | APSInt I; |
140 | bool Success = A.toIntegralConstant(Result&: I, SrcTy: D->getType(), Ctx: AST->getASTContext()); |
141 | ASSERT_TRUE(Success); |
142 | ASSERT_EQ(I, 0); |
143 | } |
144 | } |
145 | |
146 | TEST(ToAPValue, FunctionPointersC) { |
147 | // NB: The declaration of func2 is useless, but it makes us register a global |
148 | // variable for func. |
149 | constexpr char Code[] = "const int (* const func)(int *) = (void*)17;\n" |
150 | "const int (*func2)(int *) = func;\n" ; |
151 | auto AST = tooling::buildASTFromCodeWithArgs( |
152 | Code, Args: {"-x" , "c" , "-fexperimental-new-constant-interpreter" }); |
153 | |
154 | auto &Ctx = AST->getASTContext().getInterpContext(); |
155 | Program &Prog = Ctx.getProgram(); |
156 | |
157 | auto getDecl = [&](const char *Name) -> const ValueDecl * { |
158 | auto Nodes = |
159 | match(Matcher: valueDecl(hasName(Name)).bind(ID: "var" ), Context&: AST->getASTContext()); |
160 | assert(Nodes.size() == 1); |
161 | const auto *D = Nodes[0].getNodeAs<ValueDecl>(ID: "var" ); |
162 | assert(D); |
163 | return D; |
164 | }; |
165 | |
166 | auto getGlobalPtr = [&](const char *Name) -> Pointer { |
167 | const VarDecl *D = cast<VarDecl>(Val: getDecl(Name)); |
168 | return Prog.getPtrGlobal(Idx: *Prog.getGlobal(D)); |
169 | }; |
170 | |
171 | { |
172 | const ValueDecl *D = getDecl("func" ); |
173 | const Pointer &GP = getGlobalPtr("func" ); |
174 | ASSERT_TRUE(GP.isLive()); |
175 | const FunctionPointer &FP = GP.deref<FunctionPointer>(); |
176 | ASSERT_FALSE(FP.isZero()); |
177 | APValue A = FP.toAPValue(); |
178 | ASSERT_TRUE(A.hasValue()); |
179 | ASSERT_TRUE(A.isLValue()); |
180 | const auto &Path = A.getLValuePath(); |
181 | ASSERT_EQ(Path.size(), 0u); |
182 | ASSERT_TRUE(A.getLValueBase().isNull()); |
183 | APSInt I; |
184 | bool Success = A.toIntegralConstant(Result&: I, SrcTy: D->getType(), Ctx: AST->getASTContext()); |
185 | ASSERT_TRUE(Success); |
186 | ASSERT_EQ(I, 17); |
187 | } |
188 | } |
189 | |