Skip to content

Commit 9744b87

Browse files
committed
Added support for P0732r2.
1 parent e25f555 commit 9744b87

20 files changed

+695
-11
lines changed

CodeGenerator.cpp

+106-9
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,56 @@ void CodeGenerator::InsertArg(const LinkageSpecDecl* stmt)
12151215
}
12161216
//-----------------------------------------------------------------------------
12171217

1218+
void CodeGenerator::InsertTemplateArgsObjectParam(const TemplateParamObjectDecl& param)
1219+
{
1220+
PrintingPolicy pp{GetGlobalAST().getLangOpts()};
1221+
pp.adjustForCPlusPlus();
1222+
1223+
if(auto varName = GetName(param); not mSeenDecls.contains(varName)) {
1224+
std::string init{};
1225+
::llvm::raw_string_ostream stream{init};
1226+
param.printAsInit(stream, pp);
1227+
1228+
// https://eel.is/c++draft/temp.param#8 says the variable is `static const`. However, to make the
1229+
// compiler accept the generated code the storage object must be constexpr.
1230+
// The initialization itself is on the lowest level, int's, floating point or nested structs with them. For
1231+
// classes this could fail a all fields even the hidden ones are observed. However, for NTTPs the rule is that
1232+
// only structs/classes with _only_ public data members are accepted.
1233+
mOutputFormatHelper.AppendSemiNewLine(
1234+
"static constexpr ", GetName(param.getType().getUnqualifiedType()), " ", varName, init);
1235+
mSeenDecls[varName] = true;
1236+
}
1237+
}
1238+
//-----------------------------------------------------------------------------
1239+
1240+
void CodeGenerator::InsertTemplateArgsObjectParam(const ArrayRef<TemplateArgument>& array)
1241+
{
1242+
for(const auto& arg : array) {
1243+
if(TemplateArgument::Declaration != arg.getKind()) {
1244+
continue;
1245+
} else if(const auto decl = dyn_cast_or_null<TemplateParamObjectDecl>(arg.getAsDecl())) {
1246+
InsertTemplateArgsObjectParam(*decl);
1247+
}
1248+
}
1249+
}
1250+
//-----------------------------------------------------------------------------
1251+
1252+
void CodeGenerator::InsertTemplateSpecializationHeader(const Decl& decl)
1253+
{
1254+
if(const auto* fd = dyn_cast_or_null<FunctionDecl>(&decl)) {
1255+
if(const auto* specArgs = fd->getTemplateSpecializationArgs()) {
1256+
InsertTemplateArgsObjectParam(specArgs->asArray());
1257+
}
1258+
} else if(const auto* vd = dyn_cast_or_null<VarTemplateSpecializationDecl>(&decl)) {
1259+
InsertTemplateArgsObjectParam(vd->getTemplateArgs().asArray());
1260+
} else if(const auto* clsTemplateSpe = dyn_cast_or_null<ClassTemplateSpecializationDecl>(&decl)) {
1261+
InsertTemplateArgsObjectParam(clsTemplateSpe->getTemplateArgs().asArray());
1262+
}
1263+
1264+
mOutputFormatHelper.AppendNewLine(kwTemplate, "<>"sv);
1265+
}
1266+
//-----------------------------------------------------------------------------
1267+
12181268
void CodeGenerator::InsertArg(const VarDecl* stmt)
12191269
{
12201270
if(auto* init = stmt->getInit();
@@ -1258,7 +1308,7 @@ void CodeGenerator::InsertArg(const VarDecl* stmt)
12581308
}
12591309

12601310
if(isa<VarTemplateSpecializationDecl>(stmt)) {
1261-
InsertTemplateSpecializationHeader();
1311+
InsertTemplateSpecializationHeader(*stmt);
12621312
} else if(needsGuard) {
12631313
mOutputFormatHelper.InsertIfDefTemplateGuard();
12641314
}
@@ -1693,12 +1743,42 @@ static std::string_view EllipsisSpace(bool b)
16931743
}
16941744
//-----------------------------------------------------------------------------
16951745

1746+
/// \brief Evaluates a potential NTTP as a constant expression.
1747+
///
1748+
/// Used for C++20's struct/class as NTTP.
1749+
static std::optional<std::pair<QualType, APValue>> EvaluateNTTPAsConstantExpr(const Expr* expr)
1750+
{
1751+
expr = expr->IgnoreParenImpCasts();
1752+
1753+
// The marker when it is a C++20 class as NTTP seems to be CXXFunctionalCastExpr
1754+
if(Expr::EvalResult evalResult{};
1755+
isa<CXXFunctionalCastExpr>(expr) and
1756+
expr->EvaluateAsConstantExpr(evalResult, GetGlobalAST(), ConstantExprKind::Normal)) {
1757+
return std::pair<QualType, APValue>{expr->getType(), evalResult.Val};
1758+
}
1759+
1760+
return {};
1761+
}
1762+
//-----------------------------------------------------------------------------
1763+
16961764
void CodeGenerator::InsertTemplateParameters(const TemplateParameterList& list,
16971765
const TemplateParamsOnly templateParamsOnly)
16981766
{
16991767
const bool full{TemplateParamsOnly::No == templateParamsOnly};
17001768

17011769
if(full) {
1770+
for(const auto* param : list) {
1771+
if(const auto* nonTmplParam = dyn_cast_or_null<NonTypeTemplateParmDecl>(param);
1772+
nonTmplParam and nonTmplParam->hasDefaultArgument()) {
1773+
if(auto val =
1774+
EvaluateNTTPAsConstantExpr(nonTmplParam->getDefaultArgument().getArgument().getAsExpr())) {
1775+
auto* init = GetGlobalAST().getTemplateParamObjectDecl(val->first, val->second);
1776+
1777+
InsertTemplateArgsObjectParam(*init);
1778+
}
1779+
}
1780+
}
1781+
17021782
mOutputFormatHelper.Append(kwTemplate);
17031783
}
17041784

@@ -2283,8 +2363,11 @@ void CodeGenerator::InsertArg(const ImplicitCastExpr* stmt)
22832363

22842364
void CodeGenerator::InsertArg(const DeclRefExpr* stmt)
22852365
{
2286-
if(const auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl());
2287-
GetInsightsOptions().UseShow2C and IsReferenceType(vd)) {
2366+
if(const auto* tmplObjParam = dyn_cast_or_null<TemplateParamObjectDecl>(stmt->getDecl())) {
2367+
mOutputFormatHelper.Append(GetName(*tmplObjParam));
2368+
2369+
} else if(const auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl());
2370+
GetInsightsOptions().UseShow2C and IsReferenceType(vd)) {
22882371
const auto* init = vd->getInit();
22892372

22902373
if(const auto* dref = dyn_cast_or_null<DeclRefExpr>(init)) {
@@ -3564,7 +3647,7 @@ void CodeGenerator::InsertArg(const CXXDeductionGuideDecl* stmt)
35643647
const auto* deducedTemplate = stmt->getDeducedTemplate();
35653648

35663649
if(isSpecialization) {
3567-
InsertTemplateSpecializationHeader();
3650+
InsertTemplateSpecializationHeader(*stmt);
35683651
} else if(const auto* e = stmt->getDescribedFunctionTemplate()) {
35693652
InsertTemplateParameters(*e->getTemplateParameters());
35703653
}
@@ -3749,7 +3832,7 @@ void CodeGenerator::InsertArg(const CXXRecordDecl* stmt)
37493832
if(classTemplatePartialSpecializationDecl) {
37503833
InsertTemplateParameters(*classTemplatePartialSpecializationDecl->getTemplateParameters());
37513834
} else {
3752-
InsertTemplateSpecializationHeader();
3835+
InsertTemplateSpecializationHeader(*stmt);
37533836
}
37543837
// Render a out-of-line struct declared inside a class template
37553838
} else if(stmt->getLexicalDeclContext() != stmt->getDeclContext()) {
@@ -4460,7 +4543,11 @@ void CodeGenerator::InsertTemplateArg(const TemplateArgument& arg)
44604543
case TemplateArgument::Type: mOutputFormatHelper.Append(GetName(arg.getAsType())); break;
44614544
case TemplateArgument::Declaration:
44624545
// TODO: handle pointers
4463-
mOutputFormatHelper.Append("&"sv, GetName(*arg.getAsDecl(), QualifiedName::Yes));
4546+
if(const auto decl = dyn_cast_or_null<TemplateParamObjectDecl>(arg.getAsDecl())) {
4547+
mOutputFormatHelper.Append(GetName(*decl));
4548+
} else {
4549+
mOutputFormatHelper.Append("&"sv, GetName(*arg.getAsDecl(), QualifiedName::Yes));
4550+
}
44644551
break;
44654552
case TemplateArgument::NullPtr: mOutputFormatHelper.Append(kwNullptr); break;
44664553
case TemplateArgument::Integral:
@@ -4473,7 +4560,17 @@ void CodeGenerator::InsertTemplateArg(const TemplateArgument& arg)
44734560
}
44744561

44754562
break;
4476-
case TemplateArgument::Expression: InsertArg(arg.getAsExpr()); break;
4563+
case TemplateArgument::Expression: {
4564+
if(auto val = EvaluateNTTPAsConstantExpr(arg.getAsExpr()->IgnoreParenImpCasts())) {
4565+
mOutputFormatHelper.Append(
4566+
GetName(val->first),
4567+
BuildTemplateParamObjectName(val->second.getAsString(GetGlobalAST(), val->first)));
4568+
} else {
4569+
InsertArg(arg.getAsExpr());
4570+
}
4571+
}
4572+
4573+
break;
44774574
case TemplateArgument::Pack: HandleTemplateParameterPack(arg.pack_elements()); break;
44784575
case TemplateArgument::Template:
44794576
mOutputFormatHelper.Append(GetName(*arg.getAsTemplate().getAsTemplateDecl()));
@@ -4482,7 +4579,7 @@ void CodeGenerator::InsertTemplateArg(const TemplateArgument& arg)
44824579
mOutputFormatHelper.Append(GetName(*arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl()));
44834580
break;
44844581
case TemplateArgument::Null: mOutputFormatHelper.Append("null"sv); break;
4485-
case TemplateArgument::StructuralValue: ToDo(arg, mOutputFormatHelper); break;
4582+
case TemplateArgument::StructuralValue: mOutputFormatHelper.Append(arg.getAsStructuralValue()); break;
44864583
}
44874584
}
44884585
//-----------------------------------------------------------------------------
@@ -4733,7 +4830,7 @@ void CodeGenerator::InsertFunctionNameWithReturnType(const FunctionDecl& d
47334830

47344831
} else if(decl.isFunctionTemplateSpecialization() or (isClassTemplateSpec and decl.isOutOfLine() and
47354832
(decl.getLexicalDeclContext() != methodDecl->getParent()))) {
4736-
InsertTemplateSpecializationHeader();
4833+
InsertTemplateSpecializationHeader(decl);
47374834
}
47384835

47394836
InsertAttributes(decl.attrs());

CodeGenerator.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,10 @@ class CodeGenerator
349349
void InsertTemplateGuardEnd(const FunctionDecl* stmt);
350350

351351
/// \brief Insert \c template<> to introduce a template specialization.
352-
void InsertTemplateSpecializationHeader() { mOutputFormatHelper.AppendNewLine("template<>"sv); }
352+
void InsertTemplateSpecializationHeader(const Decl&);
353+
354+
void InsertTemplateArgsObjectParam(const ArrayRef<TemplateArgument>& array);
355+
void InsertTemplateArgsObjectParam(const TemplateParamObjectDecl& param);
353356

354357
void InsertNamespace(const NestedNameSpecifier* namespaceSpecifier);
355358
void ParseDeclContext(const DeclContext* Ctx);
@@ -453,7 +456,8 @@ class CodeGenerator
453456
nullptr}; //!< Helper output buffer for std::initializer_list expansion.
454457
bool mRequiresImplicitReturnZero{}; //!< Track whether this is a function with an imlpicit return 0.
455458
bool mSkipSemi{};
456-
ProcessingPrimaryTemplate mProcessingPrimaryTemplate{};
459+
ProcessingPrimaryTemplate mProcessingPrimaryTemplate{};
460+
static inline std::map<std::string, bool> mSeenDecls{};
457461
};
458462
//-----------------------------------------------------------------------------
459463

InsightsHelpers.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ const QualType GetDesugarType(const QualType& QT)
311311

312312
const std::string EvaluateAsFloat(const FloatingLiteral& expr)
313313
{
314+
// return std::to_string(expr.getValueAsApproximateDouble());
315+
314316
SmallString<16> str{};
315317
expr.getValue().toString(str);
316318

@@ -1129,6 +1131,28 @@ std::string GetName(const NamedDecl& nd, const QualifiedName qualifiedName)
11291131
}
11301132
//-----------------------------------------------------------------------------
11311133

1134+
std::string BuildTemplateParamObjectName(std::string name)
1135+
{
1136+
ReplaceAll(name, "{"sv, "_"sv);
1137+
ReplaceAll(name, "}"sv, "_"sv);
1138+
ReplaceAll(name, " "sv, ""sv);
1139+
ReplaceAll(name, ","sv, "_"sv);
1140+
ReplaceAll(name, "."sv, "_"sv);
1141+
ReplaceAll(name, "+"sv, "_"sv);
1142+
1143+
return name;
1144+
}
1145+
//-----------------------------------------------------------------------------
1146+
1147+
std::string GetName(const TemplateParamObjectDecl& decl)
1148+
{
1149+
StringStream stream{};
1150+
stream.Print(decl);
1151+
1152+
return ScopeHandler::RemoveCurrentScope(BuildTemplateParamObjectName(std::move(stream.str())));
1153+
}
1154+
//-----------------------------------------------------------------------------
1155+
11321156
std::string GetName(const CXXRecordDecl& RD)
11331157
{
11341158
if(RD.isLambda()) {
@@ -1580,6 +1604,12 @@ void StringStream::Print(const TemplateSpecializationType& arg)
15801604
}
15811605
//-----------------------------------------------------------------------------
15821606

1607+
void StringStream::Print(const TemplateParamObjectDecl& arg)
1608+
{
1609+
arg.printAsExpr(*this);
1610+
}
1611+
//-----------------------------------------------------------------------------
1612+
15831613
void StringStream::Print(const TypeConstraint& arg)
15841614
{
15851615
arg.print(*this, CppInsightsPrintingPolicy{});

InsightsHelpers.h

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525

2626
namespace clang::insights {
2727

28+
std::string BuildTemplateParamObjectName(std::string name);
29+
//-----------------------------------------------------------------------------
30+
2831
std::string BuildInternalVarName(const std::string_view& varName);
2932
//-----------------------------------------------------------------------------
3033

@@ -105,6 +108,7 @@ std::string GetPlainName(const DeclRefExpr& DRE);
105108

106109
std::string GetName(const DeclRefExpr& declRefExpr);
107110
std::string GetName(const VarDecl& VD);
111+
std::string GetName(const TemplateParamObjectDecl& decl);
108112
//-----------------------------------------------------------------------------
109113

110114
STRONG_BOOL(QualifiedName);
@@ -304,6 +308,7 @@ class StringStream : public ::llvm::raw_string_ostream
304308

305309
void Print(const TemplateArgument&);
306310
void Print(const TemplateSpecializationType&);
311+
void Print(const TemplateParamObjectDecl&);
307312
void Print(const TypeConstraint&);
308313
void Print(const StringLiteral&);
309314
void Print(const CharacterLiteral&);

InsightsStrCat.h

+27
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,33 @@ inline std::string Normalize(const llvm::APSInt& arg)
5151
}
5252
//-----------------------------------------------------------------------------
5353

54+
inline std::string Normalize(const APValue& arg)
55+
{
56+
switch(arg.getKind()) {
57+
case APValue::Int: return Normalize(arg.getInt());
58+
case APValue::Float: {
59+
std::string str{};
60+
::llvm::raw_string_ostream stream{str};
61+
62+
arg.getFloat().print(stream);
63+
64+
if(std::string::npos == str.find('.')) {
65+
/* in case it is a number like 10.0 toString() seems to leave out the .0. However, as this distinguished
66+
* between an integer and a floating point literal we need that dot. */
67+
str.pop_back();
68+
str.append(".0");
69+
}
70+
71+
return str;
72+
}
73+
74+
default: break;
75+
}
76+
77+
return std::string{"unsupported APValue"};
78+
}
79+
//-----------------------------------------------------------------------------
80+
5481
inline std::string_view Normalize(const StringRef& arg)
5582
{
5683
return arg;

tests/Cpp20ClassAsNTTP2Test.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// cmdline:-std=c++20
2+
3+
#include <cstdio>
4+
5+
#define INSIGHTS_USE_TEMPLATE 1
6+
7+
struct A{
8+
int x;
9+
constexpr A(int _x) : x{_x} {}
10+
};
11+
12+
template<A a>
13+
void Fun()
14+
{
15+
printf("prim\n");
16+
}
17+
18+
template<>
19+
void Fun<A{3}>()
20+
{
21+
printf("for 3\n");
22+
}
23+
24+
25+
template<A a>
26+
bool varTmplTest = a.x;
27+
28+
29+
template<bool, A a>
30+
struct ClsTmplTest
31+
{
32+
};
33+
34+
template<A a>
35+
struct ClsTmplTest<true, a>
36+
{
37+
};
38+
39+
40+
int main()
41+
{
42+
Fun<A{2}>();
43+
Fun<A{3}>();
44+
45+
46+
auto a = varTmplTest<A{4}>;
47+
auto b = varTmplTest<A{3}>; // existing A!
48+
49+
50+
ClsTmplTest<true, A{5}> clstmpl{};
51+
52+
}

0 commit comments

Comments
 (0)