Skip to content

Commit b2ea38f

Browse files
committed
[clang][Interp] Fix handling integral function pointers
As expected, we need to be a little more careful when the Function* is created from an integer.
1 parent 9c4aca2 commit b2ea38f

File tree

2 files changed

+88
-18
lines changed

2 files changed

+88
-18
lines changed

clang/lib/AST/Interp/FunctionPointer.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ namespace interp {
2020
class FunctionPointer final {
2121
private:
2222
const Function *Func;
23+
bool Valid;
2324

2425
public:
25-
// FIXME: We might want to track the fact that the Function pointer
26-
// has been created from an integer and is most likely garbage anyway.
27-
FunctionPointer(uintptr_t IntVal = 0, const Descriptor *Desc = nullptr)
28-
: Func(reinterpret_cast<const Function *>(IntVal)) {}
26+
FunctionPointer(const Function *Func) : Func(Func), Valid(true) {
27+
assert(Func);
28+
}
2929

30-
FunctionPointer(const Function *Func) : Func(Func) { assert(Func); }
30+
FunctionPointer(uintptr_t IntVal = 0, const Descriptor *Desc = nullptr)
31+
: Func(reinterpret_cast<const Function *>(IntVal)), Valid(false) {}
3132

3233
const Function *getFunction() const { return Func; }
3334
bool isZero() const { return !Func; }
@@ -37,14 +38,21 @@ class FunctionPointer final {
3738
return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {},
3839
/*OnePastTheEnd=*/false, /*IsNull=*/true);
3940

41+
if (!Valid)
42+
return APValue(static_cast<Expr *>(nullptr),
43+
CharUnits::fromQuantity(getIntegerRepresentation()), {},
44+
/*OnePastTheEnd=*/false, /*IsNull=*/false);
45+
4046
return APValue(Func->getDecl(), CharUnits::Zero(), {},
4147
/*OnePastTheEnd=*/false, /*IsNull=*/false);
4248
}
4349

4450
void print(llvm::raw_ostream &OS) const {
4551
OS << "FnPtr(";
46-
if (Func)
52+
if (Func && Valid)
4753
OS << Func->getName();
54+
else if (Func)
55+
OS << reinterpret_cast<uintptr_t>(Func);
4856
else
4957
OS << "nullptr";
5058
OS << ")";

clang/unittests/AST/Interp/toAPValue.cpp

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ TEST(ToAPValue, Pointers) {
9090

9191
TEST(ToAPValue, FunctionPointers) {
9292
constexpr char Code[] = " constexpr bool foo() { return true; }\n"
93-
" constexpr bool (*func)() = foo;\n";
93+
" constexpr bool (*func)() = foo;\n"
94+
" constexpr bool (*nullp)() = nullptr;\n";
9495

9596
auto AST = tooling::buildASTFromCodeWithArgs(
9697
Code, {"-fexperimental-new-constant-interpreter"});
@@ -112,15 +113,76 @@ TEST(ToAPValue, FunctionPointers) {
112113
return Prog.getPtrGlobal(*Prog.getGlobal(D));
113114
};
114115

115-
const Pointer &GP = getGlobalPtr("func");
116-
const FunctionPointer &FP = GP.deref<FunctionPointer>();
117-
ASSERT_FALSE(FP.isZero());
118-
APValue A = FP.toAPValue();
119-
ASSERT_TRUE(A.hasValue());
120-
ASSERT_TRUE(A.isLValue());
121-
ASSERT_TRUE(A.hasLValuePath());
122-
const auto &Path = A.getLValuePath();
123-
ASSERT_EQ(Path.size(), 0u);
124-
ASSERT_FALSE(A.getLValueBase().isNull());
125-
ASSERT_EQ(A.getLValueBase().dyn_cast<const ValueDecl *>(), getDecl("foo"));
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(I, D->getType(), 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, {"-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(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
160+
assert(Nodes.size() == 1);
161+
const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
162+
assert(D);
163+
return D;
164+
};
165+
166+
auto getGlobalPtr = [&](const char *Name) -> Pointer {
167+
const VarDecl *D = cast<VarDecl>(getDecl(Name));
168+
return Prog.getPtrGlobal(*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(I, D->getType(), AST->getASTContext());
185+
ASSERT_TRUE(Success);
186+
ASSERT_EQ(I, 17);
187+
}
126188
}

0 commit comments

Comments
 (0)