diff --git a/clang/include/clang/AST/TemplateUtils.h b/clang/include/clang/AST/TemplateUtils.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/AST/TemplateUtils.h @@ -0,0 +1,28 @@ +//===--- TemplateUtils.h - Classes for operating on Templates ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines helpers for operating on template parameters in Clang's AST. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_TEMPLATEUTILS_H +#define LLVM_CLANG_AST_TEMPLATEUTILS_H + +#include "clang/AST/ASTContext.h" + +namespace clang { +namespace TemplateUtils { +/// Make a best-effort determination of whether the type T can be produced by +/// substituting Args into the default argument of Param. +bool isSubstitutedDefaultArgument(ASTContext &Ctx, TemplateArgument Arg, + const NamedDecl *Param, + ArrayRef Args, + unsigned Depth); +} // namespace TemplateUtils +} // namespace clang +#endif diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -114,6 +114,7 @@ StmtViz.cpp TemplateBase.cpp TemplateName.cpp + TemplateUtils.cpp TextNodeDumper.cpp Type.cpp TypeLoc.cpp diff --git a/clang/lib/AST/TemplateUtils.cpp b/clang/lib/AST/TemplateUtils.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/AST/TemplateUtils.cpp @@ -0,0 +1,155 @@ +//===- TemplateUtils.cpp---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines helpers for operating on template parameters in Clang's AST. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/TemplateUtils.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/TemplateBase.h" + +using namespace clang; + +namespace { +bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg, + TemplateArgument Pattern, + ArrayRef Args, + unsigned Depth); + +bool isSubstitutedType(ASTContext &Ctx, QualType T, QualType Pattern, + ArrayRef Args, unsigned Depth) { + if (Ctx.hasSameType(T, Pattern)) + return true; + + // A type parameter matches its argument. + if (auto *TTPT = Pattern->getAs()) { + if (TTPT->getDepth() == Depth && TTPT->getIndex() < Args.size() && + Args[TTPT->getIndex()].getKind() == TemplateArgument::Type) { + QualType SubstArg = Ctx.getQualifiedType( + Args[TTPT->getIndex()].getAsType(), Pattern.getQualifiers()); + return Ctx.hasSameType(SubstArg, T); + } + return false; + } + + // FIXME: Recurse into array types. + + // All other cases will need the types to be identically qualified. + Qualifiers TQual, PatQual; + T = Ctx.getUnqualifiedArrayType(T, TQual); + Pattern = Ctx.getUnqualifiedArrayType(Pattern, PatQual); + if (TQual != PatQual) + return false; + + // Recurse into pointer-like types. + { + QualType TPointee = T->getPointeeType(); + QualType PPointee = Pattern->getPointeeType(); + if (!TPointee.isNull() && !PPointee.isNull()) + return T->getTypeClass() == Pattern->getTypeClass() && + isSubstitutedType(Ctx, TPointee, PPointee, Args, Depth); + } + + // Recurse into template specialization types. + if (auto *PTST = + Pattern.getCanonicalType()->getAs()) { + TemplateName Template; + ArrayRef TemplateArgs; + if (auto *TTST = T->getAs()) { + Template = TTST->getTemplateName(); + TemplateArgs = TTST->template_arguments(); + } else if (auto *CTSD = dyn_cast_or_null( + T->getAsCXXRecordDecl())) { + Template = TemplateName(CTSD->getSpecializedTemplate()); + TemplateArgs = CTSD->getTemplateArgs().asArray(); + } else { + return false; + } + + if (!isSubstitutedTemplateArgument(Ctx, Template, PTST->getTemplateName(), + Args, Depth)) + return false; + if (TemplateArgs.size() != PTST->template_arguments().size()) + return false; + for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I) + if (!isSubstitutedTemplateArgument( + Ctx, TemplateArgs[I], PTST->template_arguments()[I], Args, Depth)) + return false; + return true; + } + + // FIXME: Handle more cases. + return false; +} + +bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg, + TemplateArgument Pattern, + ArrayRef Args, + unsigned Depth) { + Arg = Ctx.getCanonicalTemplateArgument(Arg); + Pattern = Ctx.getCanonicalTemplateArgument(Pattern); + if (Arg.structurallyEquals(Pattern)) + return true; + + if (Pattern.getKind() == TemplateArgument::Expression) { + if (auto *DRE = + dyn_cast(Pattern.getAsExpr()->IgnoreParenImpCasts())) { + if (auto *NTTP = dyn_cast(DRE->getDecl())) + return NTTP->getDepth() == Depth && Args.size() > NTTP->getIndex() && + Args[NTTP->getIndex()].structurallyEquals(Arg); + } + } + + if (Arg.getKind() != Pattern.getKind()) + return false; + + if (Arg.getKind() == TemplateArgument::Type) + return isSubstitutedType(Ctx, Arg.getAsType(), Pattern.getAsType(), Args, + Depth); + + if (Arg.getKind() == TemplateArgument::Template) { + TemplateDecl *PatTD = Pattern.getAsTemplate().getAsTemplateDecl(); + if (auto *TTPD = dyn_cast_or_null(PatTD)) + return TTPD->getDepth() == Depth && Args.size() > TTPD->getIndex() && + Ctx.getCanonicalTemplateArgument(Args[TTPD->getIndex()]) + .structurallyEquals(Arg); + } + + // FIXME: Handle more cases. + return false; +} +} // namespace + +namespace clang::TemplateUtils { +/// Make a best-effort determination of whether the type T can be produced by +/// substituting Args into the default argument of Param. +bool isSubstitutedDefaultArgument(ASTContext &Ctx, TemplateArgument Arg, + const NamedDecl *Param, + ArrayRef Args, + unsigned Depth) { + // An empty pack is equivalent to not providing a pack argument. + if (Arg.getKind() == TemplateArgument::Pack && Arg.pack_size() == 0) + return true; + + if (auto *TTPD = dyn_cast(Param)) { + return TTPD->hasDefaultArgument() && + isSubstitutedTemplateArgument(Ctx, Arg, TTPD->getDefaultArgument(), + Args, Depth); + } else if (auto *TTPD = dyn_cast(Param)) { + return TTPD->hasDefaultArgument() && + isSubstitutedTemplateArgument( + Ctx, Arg, TTPD->getDefaultArgument().getArgument(), Args, Depth); + } else if (auto *NTTPD = dyn_cast(Param)) { + return NTTPD->hasDefaultArgument() && + isSubstitutedTemplateArgument(Ctx, Arg, NTTPD->getDefaultArgument(), + Args, Depth); + } + return false; +} +} // namespace clang::TemplateUtils diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -22,6 +22,7 @@ #include "clang/AST/PrettyPrinter.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" +#include "clang/AST/TemplateUtils.h" #include "clang/AST/TextNodeDumper.h" #include "clang/AST/Type.h" #include "clang/Basic/AddressSpaces.h" @@ -1936,140 +1937,6 @@ return A.getArgument().print(PP, OS, IncludeType); } -static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg, - TemplateArgument Pattern, - ArrayRef Args, - unsigned Depth); - -static bool isSubstitutedType(ASTContext &Ctx, QualType T, QualType Pattern, - ArrayRef Args, unsigned Depth) { - if (Ctx.hasSameType(T, Pattern)) - return true; - - // A type parameter matches its argument. - if (auto *TTPT = Pattern->getAs()) { - if (TTPT->getDepth() == Depth && TTPT->getIndex() < Args.size() && - Args[TTPT->getIndex()].getKind() == TemplateArgument::Type) { - QualType SubstArg = Ctx.getQualifiedType( - Args[TTPT->getIndex()].getAsType(), Pattern.getQualifiers()); - return Ctx.hasSameType(SubstArg, T); - } - return false; - } - - // FIXME: Recurse into array types. - - // All other cases will need the types to be identically qualified. - Qualifiers TQual, PatQual; - T = Ctx.getUnqualifiedArrayType(T, TQual); - Pattern = Ctx.getUnqualifiedArrayType(Pattern, PatQual); - if (TQual != PatQual) - return false; - - // Recurse into pointer-like types. - { - QualType TPointee = T->getPointeeType(); - QualType PPointee = Pattern->getPointeeType(); - if (!TPointee.isNull() && !PPointee.isNull()) - return T->getTypeClass() == Pattern->getTypeClass() && - isSubstitutedType(Ctx, TPointee, PPointee, Args, Depth); - } - - // Recurse into template specialization types. - if (auto *PTST = - Pattern.getCanonicalType()->getAs()) { - TemplateName Template; - ArrayRef TemplateArgs; - if (auto *TTST = T->getAs()) { - Template = TTST->getTemplateName(); - TemplateArgs = TTST->template_arguments(); - } else if (auto *CTSD = dyn_cast_or_null( - T->getAsCXXRecordDecl())) { - Template = TemplateName(CTSD->getSpecializedTemplate()); - TemplateArgs = CTSD->getTemplateArgs().asArray(); - } else { - return false; - } - - if (!isSubstitutedTemplateArgument(Ctx, Template, PTST->getTemplateName(), - Args, Depth)) - return false; - if (TemplateArgs.size() != PTST->template_arguments().size()) - return false; - for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I) - if (!isSubstitutedTemplateArgument( - Ctx, TemplateArgs[I], PTST->template_arguments()[I], Args, Depth)) - return false; - return true; - } - - // FIXME: Handle more cases. - return false; -} - -static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg, - TemplateArgument Pattern, - ArrayRef Args, - unsigned Depth) { - Arg = Ctx.getCanonicalTemplateArgument(Arg); - Pattern = Ctx.getCanonicalTemplateArgument(Pattern); - if (Arg.structurallyEquals(Pattern)) - return true; - - if (Pattern.getKind() == TemplateArgument::Expression) { - if (auto *DRE = - dyn_cast(Pattern.getAsExpr()->IgnoreParenImpCasts())) { - if (auto *NTTP = dyn_cast(DRE->getDecl())) - return NTTP->getDepth() == Depth && Args.size() > NTTP->getIndex() && - Args[NTTP->getIndex()].structurallyEquals(Arg); - } - } - - if (Arg.getKind() != Pattern.getKind()) - return false; - - if (Arg.getKind() == TemplateArgument::Type) - return isSubstitutedType(Ctx, Arg.getAsType(), Pattern.getAsType(), Args, - Depth); - - if (Arg.getKind() == TemplateArgument::Template) { - TemplateDecl *PatTD = Pattern.getAsTemplate().getAsTemplateDecl(); - if (auto *TTPD = dyn_cast_or_null(PatTD)) - return TTPD->getDepth() == Depth && Args.size() > TTPD->getIndex() && - Ctx.getCanonicalTemplateArgument(Args[TTPD->getIndex()]) - .structurallyEquals(Arg); - } - - // FIXME: Handle more cases. - return false; -} - -/// Make a best-effort determination of whether the type T can be produced by -/// substituting Args into the default argument of Param. -static bool isSubstitutedDefaultArgument(ASTContext &Ctx, TemplateArgument Arg, - const NamedDecl *Param, - ArrayRef Args, - unsigned Depth) { - // An empty pack is equivalent to not providing a pack argument. - if (Arg.getKind() == TemplateArgument::Pack && Arg.pack_size() == 0) - return true; - - if (auto *TTPD = dyn_cast(Param)) { - return TTPD->hasDefaultArgument() && - isSubstitutedTemplateArgument(Ctx, Arg, TTPD->getDefaultArgument(), - Args, Depth); - } else if (auto *TTPD = dyn_cast(Param)) { - return TTPD->hasDefaultArgument() && - isSubstitutedTemplateArgument( - Ctx, Arg, TTPD->getDefaultArgument().getArgument(), Args, Depth); - } else if (auto *NTTPD = dyn_cast(Param)) { - return NTTPD->hasDefaultArgument() && - isSubstitutedTemplateArgument(Ctx, Arg, NTTPD->getDefaultArgument(), - Args, Depth); - } - return false; -} - template static void printTo(raw_ostream &OS, ArrayRef Args, const PrintingPolicy &Policy, @@ -2083,9 +1950,9 @@ for (const TA &A : Args) OrigArgs.push_back(getArgument(A)); while (!Args.empty() && - isSubstitutedDefaultArgument(Ctx, getArgument(Args.back()), - TPL->getParam(Args.size() - 1), - OrigArgs, TPL->getDepth())) + TemplateUtils::isSubstitutedDefaultArgument( + Ctx, getArgument(Args.back()), TPL->getParam(Args.size() - 1), + OrigArgs, TPL->getDepth())) Args = Args.drop_back(); }