Skip to content

Commit

Permalink
Merge pull request #696 from andreasfertig/p0732
Browse files Browse the repository at this point in the history
Added support for P0732r2.
  • Loading branch information
andreasfertig authored Jan 27, 2025
2 parents e25f555 + 9744b87 commit cb44b7d
Show file tree
Hide file tree
Showing 20 changed files with 695 additions and 11 deletions.
115 changes: 106 additions & 9 deletions CodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,56 @@ void CodeGenerator::InsertArg(const LinkageSpecDecl* stmt)
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertTemplateArgsObjectParam(const TemplateParamObjectDecl& param)
{
PrintingPolicy pp{GetGlobalAST().getLangOpts()};
pp.adjustForCPlusPlus();

if(auto varName = GetName(param); not mSeenDecls.contains(varName)) {
std::string init{};
::llvm::raw_string_ostream stream{init};
param.printAsInit(stream, pp);

// https://eel.is/c++draft/temp.param#8 says the variable is `static const`. However, to make the
// compiler accept the generated code the storage object must be constexpr.
// The initialization itself is on the lowest level, int's, floating point or nested structs with them. For
// classes this could fail a all fields even the hidden ones are observed. However, for NTTPs the rule is that
// only structs/classes with _only_ public data members are accepted.
mOutputFormatHelper.AppendSemiNewLine(
"static constexpr ", GetName(param.getType().getUnqualifiedType()), " ", varName, init);
mSeenDecls[varName] = true;
}
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertTemplateArgsObjectParam(const ArrayRef<TemplateArgument>& array)
{
for(const auto& arg : array) {
if(TemplateArgument::Declaration != arg.getKind()) {
continue;
} else if(const auto decl = dyn_cast_or_null<TemplateParamObjectDecl>(arg.getAsDecl())) {
InsertTemplateArgsObjectParam(*decl);
}
}
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertTemplateSpecializationHeader(const Decl& decl)
{
if(const auto* fd = dyn_cast_or_null<FunctionDecl>(&decl)) {
if(const auto* specArgs = fd->getTemplateSpecializationArgs()) {
InsertTemplateArgsObjectParam(specArgs->asArray());
}
} else if(const auto* vd = dyn_cast_or_null<VarTemplateSpecializationDecl>(&decl)) {
InsertTemplateArgsObjectParam(vd->getTemplateArgs().asArray());
} else if(const auto* clsTemplateSpe = dyn_cast_or_null<ClassTemplateSpecializationDecl>(&decl)) {
InsertTemplateArgsObjectParam(clsTemplateSpe->getTemplateArgs().asArray());
}

mOutputFormatHelper.AppendNewLine(kwTemplate, "<>"sv);
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertArg(const VarDecl* stmt)
{
if(auto* init = stmt->getInit();
Expand Down Expand Up @@ -1258,7 +1308,7 @@ void CodeGenerator::InsertArg(const VarDecl* stmt)
}

if(isa<VarTemplateSpecializationDecl>(stmt)) {
InsertTemplateSpecializationHeader();
InsertTemplateSpecializationHeader(*stmt);
} else if(needsGuard) {
mOutputFormatHelper.InsertIfDefTemplateGuard();
}
Expand Down Expand Up @@ -1693,12 +1743,42 @@ static std::string_view EllipsisSpace(bool b)
}
//-----------------------------------------------------------------------------

/// \brief Evaluates a potential NTTP as a constant expression.
///
/// Used for C++20's struct/class as NTTP.
static std::optional<std::pair<QualType, APValue>> EvaluateNTTPAsConstantExpr(const Expr* expr)
{
expr = expr->IgnoreParenImpCasts();

// The marker when it is a C++20 class as NTTP seems to be CXXFunctionalCastExpr
if(Expr::EvalResult evalResult{};
isa<CXXFunctionalCastExpr>(expr) and
expr->EvaluateAsConstantExpr(evalResult, GetGlobalAST(), ConstantExprKind::Normal)) {
return std::pair<QualType, APValue>{expr->getType(), evalResult.Val};
}

return {};
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertTemplateParameters(const TemplateParameterList& list,
const TemplateParamsOnly templateParamsOnly)
{
const bool full{TemplateParamsOnly::No == templateParamsOnly};

if(full) {
for(const auto* param : list) {
if(const auto* nonTmplParam = dyn_cast_or_null<NonTypeTemplateParmDecl>(param);
nonTmplParam and nonTmplParam->hasDefaultArgument()) {
if(auto val =
EvaluateNTTPAsConstantExpr(nonTmplParam->getDefaultArgument().getArgument().getAsExpr())) {
auto* init = GetGlobalAST().getTemplateParamObjectDecl(val->first, val->second);

InsertTemplateArgsObjectParam(*init);
}
}
}

mOutputFormatHelper.Append(kwTemplate);
}

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

void CodeGenerator::InsertArg(const DeclRefExpr* stmt)
{
if(const auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl());
GetInsightsOptions().UseShow2C and IsReferenceType(vd)) {
if(const auto* tmplObjParam = dyn_cast_or_null<TemplateParamObjectDecl>(stmt->getDecl())) {
mOutputFormatHelper.Append(GetName(*tmplObjParam));

} else if(const auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl());
GetInsightsOptions().UseShow2C and IsReferenceType(vd)) {
const auto* init = vd->getInit();

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

if(isSpecialization) {
InsertTemplateSpecializationHeader();
InsertTemplateSpecializationHeader(*stmt);
} else if(const auto* e = stmt->getDescribedFunctionTemplate()) {
InsertTemplateParameters(*e->getTemplateParameters());
}
Expand Down Expand Up @@ -3749,7 +3832,7 @@ void CodeGenerator::InsertArg(const CXXRecordDecl* stmt)
if(classTemplatePartialSpecializationDecl) {
InsertTemplateParameters(*classTemplatePartialSpecializationDecl->getTemplateParameters());
} else {
InsertTemplateSpecializationHeader();
InsertTemplateSpecializationHeader(*stmt);
}
// Render a out-of-line struct declared inside a class template
} else if(stmt->getLexicalDeclContext() != stmt->getDeclContext()) {
Expand Down Expand Up @@ -4460,7 +4543,11 @@ void CodeGenerator::InsertTemplateArg(const TemplateArgument& arg)
case TemplateArgument::Type: mOutputFormatHelper.Append(GetName(arg.getAsType())); break;
case TemplateArgument::Declaration:
// TODO: handle pointers
mOutputFormatHelper.Append("&"sv, GetName(*arg.getAsDecl(), QualifiedName::Yes));
if(const auto decl = dyn_cast_or_null<TemplateParamObjectDecl>(arg.getAsDecl())) {
mOutputFormatHelper.Append(GetName(*decl));
} else {
mOutputFormatHelper.Append("&"sv, GetName(*arg.getAsDecl(), QualifiedName::Yes));
}
break;
case TemplateArgument::NullPtr: mOutputFormatHelper.Append(kwNullptr); break;
case TemplateArgument::Integral:
Expand All @@ -4473,7 +4560,17 @@ void CodeGenerator::InsertTemplateArg(const TemplateArgument& arg)
}

break;
case TemplateArgument::Expression: InsertArg(arg.getAsExpr()); break;
case TemplateArgument::Expression: {
if(auto val = EvaluateNTTPAsConstantExpr(arg.getAsExpr()->IgnoreParenImpCasts())) {
mOutputFormatHelper.Append(
GetName(val->first),
BuildTemplateParamObjectName(val->second.getAsString(GetGlobalAST(), val->first)));
} else {
InsertArg(arg.getAsExpr());
}
}

break;
case TemplateArgument::Pack: HandleTemplateParameterPack(arg.pack_elements()); break;
case TemplateArgument::Template:
mOutputFormatHelper.Append(GetName(*arg.getAsTemplate().getAsTemplateDecl()));
Expand All @@ -4482,7 +4579,7 @@ void CodeGenerator::InsertTemplateArg(const TemplateArgument& arg)
mOutputFormatHelper.Append(GetName(*arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl()));
break;
case TemplateArgument::Null: mOutputFormatHelper.Append("null"sv); break;
case TemplateArgument::StructuralValue: ToDo(arg, mOutputFormatHelper); break;
case TemplateArgument::StructuralValue: mOutputFormatHelper.Append(arg.getAsStructuralValue()); break;
}
}
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -4733,7 +4830,7 @@ void CodeGenerator::InsertFunctionNameWithReturnType(const FunctionDecl& d

} else if(decl.isFunctionTemplateSpecialization() or (isClassTemplateSpec and decl.isOutOfLine() and
(decl.getLexicalDeclContext() != methodDecl->getParent()))) {
InsertTemplateSpecializationHeader();
InsertTemplateSpecializationHeader(decl);
}

InsertAttributes(decl.attrs());
Expand Down
8 changes: 6 additions & 2 deletions CodeGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,10 @@ class CodeGenerator
void InsertTemplateGuardEnd(const FunctionDecl* stmt);

/// \brief Insert \c template<> to introduce a template specialization.
void InsertTemplateSpecializationHeader() { mOutputFormatHelper.AppendNewLine("template<>"sv); }
void InsertTemplateSpecializationHeader(const Decl&);

void InsertTemplateArgsObjectParam(const ArrayRef<TemplateArgument>& array);
void InsertTemplateArgsObjectParam(const TemplateParamObjectDecl& param);

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

Expand Down
30 changes: 30 additions & 0 deletions InsightsHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ const QualType GetDesugarType(const QualType& QT)

const std::string EvaluateAsFloat(const FloatingLiteral& expr)
{
// return std::to_string(expr.getValueAsApproximateDouble());

SmallString<16> str{};
expr.getValue().toString(str);

Expand Down Expand Up @@ -1129,6 +1131,28 @@ std::string GetName(const NamedDecl& nd, const QualifiedName qualifiedName)
}
//-----------------------------------------------------------------------------

std::string BuildTemplateParamObjectName(std::string name)
{
ReplaceAll(name, "{"sv, "_"sv);
ReplaceAll(name, "}"sv, "_"sv);
ReplaceAll(name, " "sv, ""sv);
ReplaceAll(name, ","sv, "_"sv);
ReplaceAll(name, "."sv, "_"sv);
ReplaceAll(name, "+"sv, "_"sv);

return name;
}
//-----------------------------------------------------------------------------

std::string GetName(const TemplateParamObjectDecl& decl)
{
StringStream stream{};
stream.Print(decl);

return ScopeHandler::RemoveCurrentScope(BuildTemplateParamObjectName(std::move(stream.str())));
}
//-----------------------------------------------------------------------------

std::string GetName(const CXXRecordDecl& RD)
{
if(RD.isLambda()) {
Expand Down Expand Up @@ -1580,6 +1604,12 @@ void StringStream::Print(const TemplateSpecializationType& arg)
}
//-----------------------------------------------------------------------------

void StringStream::Print(const TemplateParamObjectDecl& arg)
{
arg.printAsExpr(*this);
}
//-----------------------------------------------------------------------------

void StringStream::Print(const TypeConstraint& arg)
{
arg.print(*this, CppInsightsPrintingPolicy{});
Expand Down
5 changes: 5 additions & 0 deletions InsightsHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@

namespace clang::insights {

std::string BuildTemplateParamObjectName(std::string name);
//-----------------------------------------------------------------------------

std::string BuildInternalVarName(const std::string_view& varName);
//-----------------------------------------------------------------------------

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

std::string GetName(const DeclRefExpr& declRefExpr);
std::string GetName(const VarDecl& VD);
std::string GetName(const TemplateParamObjectDecl& decl);
//-----------------------------------------------------------------------------

STRONG_BOOL(QualifiedName);
Expand Down Expand Up @@ -304,6 +308,7 @@ class StringStream : public ::llvm::raw_string_ostream

void Print(const TemplateArgument&);
void Print(const TemplateSpecializationType&);
void Print(const TemplateParamObjectDecl&);
void Print(const TypeConstraint&);
void Print(const StringLiteral&);
void Print(const CharacterLiteral&);
Expand Down
27 changes: 27 additions & 0 deletions InsightsStrCat.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,33 @@ inline std::string Normalize(const llvm::APSInt& arg)
}
//-----------------------------------------------------------------------------

inline std::string Normalize(const APValue& arg)
{
switch(arg.getKind()) {
case APValue::Int: return Normalize(arg.getInt());
case APValue::Float: {
std::string str{};
::llvm::raw_string_ostream stream{str};

arg.getFloat().print(stream);

if(std::string::npos == str.find('.')) {
/* in case it is a number like 10.0 toString() seems to leave out the .0. However, as this distinguished
* between an integer and a floating point literal we need that dot. */
str.pop_back();
str.append(".0");
}

return str;
}

default: break;
}

return std::string{"unsupported APValue"};
}
//-----------------------------------------------------------------------------

inline std::string_view Normalize(const StringRef& arg)
{
return arg;
Expand Down
52 changes: 52 additions & 0 deletions tests/Cpp20ClassAsNTTP2Test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// cmdline:-std=c++20

#include <cstdio>

#define INSIGHTS_USE_TEMPLATE 1

struct A{
int x;
constexpr A(int _x) : x{_x} {}
};

template<A a>
void Fun()
{
printf("prim\n");
}

template<>
void Fun<A{3}>()
{
printf("for 3\n");
}


template<A a>
bool varTmplTest = a.x;


template<bool, A a>
struct ClsTmplTest
{
};

template<A a>
struct ClsTmplTest<true, a>
{
};


int main()
{
Fun<A{2}>();
Fun<A{3}>();


auto a = varTmplTest<A{4}>;
auto b = varTmplTest<A{3}>; // existing A!


ClsTmplTest<true, A{5}> clstmpl{};

}
Loading

0 comments on commit cb44b7d

Please sign in to comment.