Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2618,6 +2618,18 @@ void get_type_as_string(QualType QT, std::string& type_name, ASTContext& C,
PrintingPolicy Policy) {
// TODO: Implement cling desugaring from utils::AST
// cling::utils::Transform::GetPartiallyDesugaredType()
// Desugar template type alias specializations (e.g. std::enable_if_t,
// std::remove_cvref_t). Their printed form can carry expression-level
// template arguments (variable-template references, SFINAE predicates)
// that PrintingPolicy::FullyQualifiedName does not propagate into, so the
// emitted text may reference identifiers like `is_constructible_v` without
// the `std::` qualifier and fail to compile in the wrapper. Regular
// typedefs (e.g. std::string) keep their sugared name.
while (const auto* TST = QT->getAs<TemplateSpecializationType>()) {
if (!TST->isTypeAlias())
break;
QT = TST->desugar();
}
if (!QT->isTypedefNameType() || QT->isBuiltinType())
QT = QT.getDesugaredType(C);
Policy.Suppress_Elab = true;
Expand Down
42 changes: 42 additions & 0 deletions unittests/CppInterOp/FunctionReflectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2289,6 +2289,48 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_GetFunctionCallWrapper) {
EXPECT_FALSE(Cpp::IsLambdaClass(Cpp::GetFunctionReturnType(bar)));
}

TYPED_TEST(CPPINTEROP_TEST_MODE,
FunctionReflection_WrapAliasTemplateReturnType) {
// Regression test for cppyy issue
// https://github.com/compiler-research/cppyy/issues/218. Building a wrapper
// for a function template whose return type is a template type alias
// specialisation (here std::enable_if_t<is_constructible_v<...>, std::any>)
// used to fail to compile: the printed return type kept the sugared alias
// form, and Clang's FullyQualifiedName printing policy does not propagate
// into the expression-level non-type template argument, so the emitted
// wrapper referenced `is_constructible_v` without the `std::` qualifier. The
// fix in get_type_as_string desugars template type alias specialisations
// before printing.
//
// Triggering Sema to keep the non-type template argument as an unevaluated
// expression (rather than folding it to a constant) requires the recursive
// SFINAE shape used by libstdc++'s std::any ctors. Hand-rolled trait classes
// get folded eagerly, so the test reaches the bug via std::make_any rather
// than a stdlib-free construction.
if (TypeParam::isOutOfProcess)
GTEST_SKIP() << "Test fails for OOP JIT builds";

std::vector<const char*> interpreter_args = {"-std=c++17", "-include", "new"};
TestFixture::CreateInterpreter(interpreter_args);
Interp->process(R"(
#include <any>
class MyClass {};
)");

// Instantiate the variadic make_any with two pointer template args.
// libstdc++'s overload set also contains an initializer_list form
// that SFINAEs out here.
Cpp::TCppFunction_t spec = Cpp::InstantiateTemplateFunctionFromString(
"std::make_any<MyClass*, MyClass*>");
ASSERT_TRUE(spec)
<< "Sema failed to substitute std::make_any<MyClass*, MyClass*>";

// Without the fix, MakeFunctionCallable fails to compile the wrapper
// and returns a JitCall of kUnknown.
Cpp::JitCall JC = Cpp::MakeFunctionCallable(spec);
EXPECT_EQ(JC.getKind(), Cpp::JitCall::kGenericCall);
}

TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsConstMethod) {
std::vector<Decl*> Decls, SubDecls;
std::string code = R"(
Expand Down
Loading