Skip to content

Commit 996075d

Browse files
authored
[clang][Interp] Handle virtual calls with covariant return types (#101218)
1 parent aa07282 commit 996075d

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed

clang/lib/AST/Interp/Interp.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2628,7 +2628,29 @@ inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
26282628
}
26292629
}
26302630

2631-
return Call(S, OpPC, Func, VarArgSize);
2631+
if (!Call(S, OpPC, Func, VarArgSize))
2632+
return false;
2633+
2634+
// Covariant return types. The return type of Overrider is a pointer
2635+
// or reference to a class type.
2636+
if (Overrider != InitialFunction &&
2637+
Overrider->getReturnType()->isPointerOrReferenceType() &&
2638+
InitialFunction->getReturnType()->isPointerOrReferenceType()) {
2639+
QualType OverriderPointeeType =
2640+
Overrider->getReturnType()->getPointeeType();
2641+
QualType InitialPointeeType =
2642+
InitialFunction->getReturnType()->getPointeeType();
2643+
// We've called Overrider above, but calling code expects us to return what
2644+
// InitialFunction returned. According to the rules for covariant return
2645+
// types, what InitialFunction returns needs to be a base class of what
2646+
// Overrider returns. So, we need to do an upcast here.
2647+
unsigned Offset = S.getContext().collectBaseOffset(
2648+
InitialPointeeType->getAsRecordDecl(),
2649+
OverriderPointeeType->getAsRecordDecl());
2650+
return GetPtrBasePop(S, OpPC, Offset);
2651+
}
2652+
2653+
return true;
26322654
}
26332655

26342656
inline bool CallBI(InterpState &S, CodePtr &PC, const Function *Func,

clang/test/AST/Interp/cxx2a.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,24 @@ consteval int aConstevalFunction() { // both-error {{consteval function never pr
1313
return 0;
1414
}
1515
/// We're NOT calling the above function. The diagnostics should appear anyway.
16+
17+
namespace Covariant {
18+
struct A {
19+
virtual constexpr char f() const { return 'Z'; }
20+
char a = f();
21+
};
22+
23+
struct D : A {};
24+
struct Covariant1 {
25+
D d;
26+
virtual const A *f() const;
27+
};
28+
29+
struct Covariant3 : Covariant1 {
30+
constexpr virtual const D *f() const { return &this->d; }
31+
};
32+
33+
constexpr Covariant3 cb;
34+
constexpr const Covariant1 *cb1 = &cb;
35+
static_assert(cb1->f()->a == 'Z');
36+
}

0 commit comments

Comments
 (0)