Skip to content

Commit e7f3e21

Browse files
committed
Suppress printing template arguments that match default template
arguments of types by default. This somewhat improves the worst-case printing of types like std::string, std::vector, etc., where many irrelevant default arguments can be included in the type as printed if we've lost the type sugar.
1 parent 686d8a0 commit e7f3e21

File tree

11 files changed

+247
-27
lines changed

11 files changed

+247
-27
lines changed

clang/include/clang/AST/PrettyPrinter.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ struct PrintingPolicy {
5555
SuppressInitializers(false), ConstantArraySizeAsWritten(false),
5656
AnonymousTagLocations(true), SuppressStrongLifetime(false),
5757
SuppressLifetimeQualifiers(false),
58-
SuppressTemplateArgsInCXXConstructors(false), Bool(LO.Bool),
58+
SuppressTemplateArgsInCXXConstructors(false),
59+
SuppressDefaultTemplateArgs(true), Bool(LO.Bool),
5960
Nullptr(LO.CPlusPlus11), Restrict(LO.C99), Alignof(LO.CPlusPlus11),
6061
UnderscoreAlignof(LO.C11), UseVoidForZeroParams(!LO.CPlusPlus),
6162
SplitTemplateClosers(!LO.CPlusPlus11), TerseOutput(false),
@@ -167,6 +168,10 @@ struct PrintingPolicy {
167168
/// constructors.
168169
unsigned SuppressTemplateArgsInCXXConstructors : 1;
169170

171+
/// When true, attempt to suppress template arguments that match the default
172+
/// argument for the parameter.
173+
unsigned SuppressDefaultTemplateArgs : 1;
174+
170175
/// Whether we can use 'bool' rather than '_Bool' (even if the language
171176
/// doesn't actually have 'bool', because, e.g., it is defined as a macro).
172177
unsigned Bool : 1;

clang/include/clang/AST/Type.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class ExtQuals;
6161
class QualType;
6262
class ConceptDecl;
6363
class TagDecl;
64+
class TemplateParameterList;
6465
class Type;
6566

6667
enum {
@@ -5196,15 +5197,18 @@ class alignas(8) TemplateSpecializationType
51965197
/// enclosing the template arguments.
51975198
void printTemplateArgumentList(raw_ostream &OS,
51985199
ArrayRef<TemplateArgument> Args,
5199-
const PrintingPolicy &Policy);
5200+
const PrintingPolicy &Policy,
5201+
const TemplateParameterList *TPL = nullptr);
52005202

52015203
void printTemplateArgumentList(raw_ostream &OS,
52025204
ArrayRef<TemplateArgumentLoc> Args,
5203-
const PrintingPolicy &Policy);
5205+
const PrintingPolicy &Policy,
5206+
const TemplateParameterList *TPL = nullptr);
52045207

52055208
void printTemplateArgumentList(raw_ostream &OS,
52065209
const TemplateArgumentListInfo &Args,
5207-
const PrintingPolicy &Policy);
5210+
const PrintingPolicy &Policy,
5211+
const TemplateParameterList *TPL = nullptr);
52085212

52095213
/// The injected class name of a C++ class template or class
52105214
/// template partial specialization. Used to record that a type was

clang/lib/AST/DeclTemplate.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -914,10 +914,14 @@ void ClassTemplateSpecializationDecl::getNameForDiagnostic(
914914
const auto *PS = dyn_cast<ClassTemplatePartialSpecializationDecl>(this);
915915
if (const ASTTemplateArgumentListInfo *ArgsAsWritten =
916916
PS ? PS->getTemplateArgsAsWritten() : nullptr) {
917-
printTemplateArgumentList(OS, ArgsAsWritten->arguments(), Policy);
917+
printTemplateArgumentList(
918+
OS, ArgsAsWritten->arguments(), Policy,
919+
getSpecializedTemplate()->getTemplateParameters());
918920
} else {
919921
const TemplateArgumentList &TemplateArgs = getTemplateArgs();
920-
printTemplateArgumentList(OS, TemplateArgs.asArray(), Policy);
922+
printTemplateArgumentList(
923+
OS, TemplateArgs.asArray(), Policy,
924+
getSpecializedTemplate()->getTemplateParameters());
921925
}
922926
}
923927

@@ -1261,10 +1265,14 @@ void VarTemplateSpecializationDecl::getNameForDiagnostic(
12611265
const auto *PS = dyn_cast<VarTemplatePartialSpecializationDecl>(this);
12621266
if (const ASTTemplateArgumentListInfo *ArgsAsWritten =
12631267
PS ? PS->getTemplateArgsAsWritten() : nullptr) {
1264-
printTemplateArgumentList(OS, ArgsAsWritten->arguments(), Policy);
1268+
printTemplateArgumentList(
1269+
OS, ArgsAsWritten->arguments(), Policy,
1270+
getSpecializedTemplate()->getTemplateParameters());
12651271
} else {
12661272
const TemplateArgumentList &TemplateArgs = getTemplateArgs();
1267-
printTemplateArgumentList(OS, TemplateArgs.asArray(), Policy);
1273+
printTemplateArgumentList(
1274+
OS, TemplateArgs.asArray(), Policy,
1275+
getSpecializedTemplate()->getTemplateParameters());
12681276
}
12691277
}
12701278

clang/lib/AST/TypePrinter.cpp

Lines changed: 175 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,7 +1124,9 @@ void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) {
11241124
OS << T->getTypeConstraintConcept()->getName();
11251125
auto Args = T->getTypeConstraintArguments();
11261126
if (!Args.empty())
1127-
printTemplateArgumentList(OS, Args, Policy);
1127+
printTemplateArgumentList(
1128+
OS, Args, Policy,
1129+
T->getTypeConstraintConcept()->getTemplateParameters());
11281130
OS << ' ';
11291131
}
11301132
switch (T->getKeyword()) {
@@ -1226,7 +1228,9 @@ void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) {
12261228
IncludeStrongLifetimeRAII Strong(Policy);
12271229
OS << Spec->getIdentifier()->getName();
12281230
const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
1229-
printTemplateArgumentList(OS, TemplateArgs.asArray(), Policy);
1231+
printTemplateArgumentList(
1232+
OS, TemplateArgs.asArray(), Policy,
1233+
Spec->getSpecializedTemplate()->getTemplateParameters());
12301234
OS << "::";
12311235
} else if (const auto *Tag = dyn_cast<TagDecl>(DC)) {
12321236
if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl())
@@ -1317,7 +1321,9 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) {
13171321
Args = TemplateArgs.asArray();
13181322
}
13191323
IncludeStrongLifetimeRAII Strong(Policy);
1320-
printTemplateArgumentList(OS, Args, Policy);
1324+
printTemplateArgumentList(
1325+
OS, Args, Policy,
1326+
Spec->getSpecializedTemplate()->getTemplateParameters());
13211327
}
13221328

13231329
spaceBeforePlaceHolder(OS);
@@ -1389,7 +1395,11 @@ void TypePrinter::printTemplateSpecializationBefore(
13891395
IncludeStrongLifetimeRAII Strong(Policy);
13901396
T->getTemplateName().print(OS, Policy);
13911397

1392-
printTemplateArgumentList(OS, T->template_arguments(), Policy);
1398+
const TemplateParameterList *TPL = nullptr;
1399+
if (TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl())
1400+
TPL = TD->getTemplateParameters();
1401+
1402+
printTemplateArgumentList(OS, T->template_arguments(), Policy, TPL);
13931403
spaceBeforePlaceHolder(OS);
13941404
}
13951405

@@ -1789,9 +1799,159 @@ static void printArgument(const TemplateArgumentLoc &A,
17891799
return A.getArgument().print(PP, OS);
17901800
}
17911801

1802+
static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg,
1803+
TemplateArgument Pattern,
1804+
ArrayRef<TemplateArgument> Args,
1805+
unsigned Depth);
1806+
1807+
static bool isSubstitutedType(ASTContext &Ctx, QualType T, QualType Pattern,
1808+
ArrayRef<TemplateArgument> Args, unsigned Depth) {
1809+
if (Ctx.hasSameType(T, Pattern))
1810+
return true;
1811+
1812+
// A type parameter matches its argument.
1813+
if (auto *TTPT = Pattern->getAs<TemplateTypeParmType>()) {
1814+
if (TTPT->getDepth() == Depth && TTPT->getIndex() < Args.size() &&
1815+
Args[TTPT->getIndex()].getKind() == TemplateArgument::Type) {
1816+
QualType SubstArg = Ctx.getQualifiedType(
1817+
Args[TTPT->getIndex()].getAsType(), Pattern.getQualifiers());
1818+
return Ctx.hasSameType(SubstArg, T);
1819+
}
1820+
return false;
1821+
}
1822+
1823+
// FIXME: Recurse into array types.
1824+
1825+
// All other cases will need the types to be identically qualified.
1826+
Qualifiers TQual, PatQual;
1827+
T = Ctx.getUnqualifiedArrayType(T, TQual);
1828+
Pattern = Ctx.getUnqualifiedArrayType(Pattern, PatQual);
1829+
if (TQual != PatQual)
1830+
return false;
1831+
1832+
// Recurse into pointer-like types.
1833+
{
1834+
QualType TPointee = T->getPointeeType();
1835+
QualType PPointee = Pattern->getPointeeType();
1836+
if (!TPointee.isNull() && !PPointee.isNull())
1837+
return T->getTypeClass() == Pattern->getTypeClass() &&
1838+
isSubstitutedType(Ctx, TPointee, PPointee, Args, Depth);
1839+
}
1840+
1841+
// Recurse into template specialization types.
1842+
if (auto *PTST =
1843+
Pattern.getCanonicalType()->getAs<TemplateSpecializationType>()) {
1844+
TemplateName Template;
1845+
ArrayRef<TemplateArgument> TemplateArgs;
1846+
if (auto *TTST = T->getAs<TemplateSpecializationType>()) {
1847+
Template = TTST->getTemplateName();
1848+
TemplateArgs = TTST->template_arguments();
1849+
} else if (auto *CTSD = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
1850+
T->getAsCXXRecordDecl())) {
1851+
Template = TemplateName(CTSD->getSpecializedTemplate());
1852+
TemplateArgs = CTSD->getTemplateArgs().asArray();
1853+
} else {
1854+
return false;
1855+
}
1856+
1857+
if (!isSubstitutedTemplateArgument(Ctx, Template, PTST->getTemplateName(),
1858+
Args, Depth))
1859+
return false;
1860+
if (TemplateArgs.size() != PTST->getNumArgs())
1861+
return false;
1862+
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
1863+
if (!isSubstitutedTemplateArgument(Ctx, TemplateArgs[I], PTST->getArg(I),
1864+
Args, Depth))
1865+
return false;
1866+
return true;
1867+
}
1868+
1869+
// FIXME: Handle more cases.
1870+
return false;
1871+
}
1872+
1873+
static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg,
1874+
TemplateArgument Pattern,
1875+
ArrayRef<TemplateArgument> Args,
1876+
unsigned Depth) {
1877+
Arg = Ctx.getCanonicalTemplateArgument(Arg);
1878+
Pattern = Ctx.getCanonicalTemplateArgument(Pattern);
1879+
if (Arg.structurallyEquals(Pattern))
1880+
return true;
1881+
1882+
if (Pattern.getKind() == TemplateArgument::Expression) {
1883+
if (auto *DRE =
1884+
dyn_cast<DeclRefExpr>(Pattern.getAsExpr()->IgnoreParenImpCasts())) {
1885+
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl()))
1886+
return NTTP->getDepth() == Depth && Args.size() > NTTP->getIndex() &&
1887+
Args[NTTP->getIndex()].structurallyEquals(Arg);
1888+
}
1889+
}
1890+
1891+
if (Arg.getKind() != Pattern.getKind())
1892+
return false;
1893+
1894+
if (Arg.getKind() == TemplateArgument::Type)
1895+
return isSubstitutedType(Ctx, Arg.getAsType(), Pattern.getAsType(), Args,
1896+
Depth);
1897+
1898+
if (Arg.getKind() == TemplateArgument::Template) {
1899+
TemplateDecl *PatTD = Pattern.getAsTemplate().getAsTemplateDecl();
1900+
if (auto *TTPD = dyn_cast_or_null<TemplateTemplateParmDecl>(PatTD))
1901+
return TTPD->getDepth() == Depth && Args.size() > TTPD->getIndex() &&
1902+
Ctx.getCanonicalTemplateArgument(Args[TTPD->getIndex()])
1903+
.structurallyEquals(Arg);
1904+
}
1905+
1906+
// FIXME: Handle more cases.
1907+
return false;
1908+
}
1909+
1910+
/// Make a best-effort determination of whether the type T can be produced by
1911+
/// substituting Args into the default argument of Param.
1912+
static bool isSubstitutedDefaultArgument(ASTContext &Ctx, TemplateArgument Arg,
1913+
const NamedDecl *Param,
1914+
ArrayRef<TemplateArgument> Args,
1915+
unsigned Depth) {
1916+
// An empty pack is equivalent to not providing a pack argument.
1917+
if (Arg.getKind() == TemplateArgument::Pack && Arg.pack_size() == 0)
1918+
return true;
1919+
1920+
if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(Param)) {
1921+
return TTPD->hasDefaultArgument() &&
1922+
isSubstitutedTemplateArgument(Ctx, Arg, TTPD->getDefaultArgument(),
1923+
Args, Depth);
1924+
} else if (auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) {
1925+
return TTPD->hasDefaultArgument() &&
1926+
isSubstitutedTemplateArgument(
1927+
Ctx, Arg, TTPD->getDefaultArgument().getArgument(), Args, Depth);
1928+
} else if (auto *NTTPD = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
1929+
return NTTPD->hasDefaultArgument() &&
1930+
isSubstitutedTemplateArgument(Ctx, Arg, NTTPD->getDefaultArgument(),
1931+
Args, Depth);
1932+
}
1933+
return false;
1934+
}
1935+
17921936
template<typename TA>
17931937
static void printTo(raw_ostream &OS, ArrayRef<TA> Args,
1794-
const PrintingPolicy &Policy, bool SkipBrackets) {
1938+
const PrintingPolicy &Policy, bool SkipBrackets,
1939+
const TemplateParameterList *TPL) {
1940+
// Drop trailing template arguments that match default arguments.
1941+
if (TPL && Policy.SuppressDefaultTemplateArgs &&
1942+
!Policy.PrintCanonicalTypes && !Args.empty() &&
1943+
Args.size() <= TPL->size()) {
1944+
ASTContext &Ctx = TPL->getParam(0)->getASTContext();
1945+
llvm::SmallVector<TemplateArgument, 8> OrigArgs;
1946+
for (const TA &A : Args)
1947+
OrigArgs.push_back(getArgument(A));
1948+
while (!Args.empty() &&
1949+
isSubstitutedDefaultArgument(Ctx, getArgument(Args.back()),
1950+
TPL->getParam(Args.size() - 1),
1951+
OrigArgs, TPL->getDepth()))
1952+
Args = Args.drop_back();
1953+
}
1954+
17951955
const char *Comma = Policy.MSVCFormatting ? "," : ", ";
17961956
if (!SkipBrackets)
17971957
OS << '<';
@@ -1806,7 +1966,7 @@ static void printTo(raw_ostream &OS, ArrayRef<TA> Args,
18061966
if (Argument.getKind() == TemplateArgument::Pack) {
18071967
if (Argument.pack_size() && !FirstArg)
18081968
OS << Comma;
1809-
printTo(ArgOS, Argument.getPackAsArray(), Policy, true);
1969+
printTo(ArgOS, Argument.getPackAsArray(), Policy, true, nullptr);
18101970
} else {
18111971
if (!FirstArg)
18121972
OS << Comma;
@@ -1839,20 +1999,23 @@ static void printTo(raw_ostream &OS, ArrayRef<TA> Args,
18391999

18402000
void clang::printTemplateArgumentList(raw_ostream &OS,
18412001
const TemplateArgumentListInfo &Args,
1842-
const PrintingPolicy &Policy) {
1843-
return printTo(OS, Args.arguments(), Policy, false);
2002+
const PrintingPolicy &Policy,
2003+
const TemplateParameterList *TPL) {
2004+
printTemplateArgumentList(OS, Args.arguments(), Policy, TPL);
18442005
}
18452006

18462007
void clang::printTemplateArgumentList(raw_ostream &OS,
18472008
ArrayRef<TemplateArgument> Args,
1848-
const PrintingPolicy &Policy) {
1849-
printTo(OS, Args, Policy, false);
2009+
const PrintingPolicy &Policy,
2010+
const TemplateParameterList *TPL) {
2011+
printTo(OS, Args, Policy, false, TPL);
18502012
}
18512013

18522014
void clang::printTemplateArgumentList(raw_ostream &OS,
18532015
ArrayRef<TemplateArgumentLoc> Args,
1854-
const PrintingPolicy &Policy) {
1855-
printTo(OS, Args, Policy, false);
2016+
const PrintingPolicy &Policy,
2017+
const TemplateParameterList *TPL) {
2018+
printTo(OS, Args, Policy, false, TPL);
18562019
}
18572020

18582021
std::string Qualifiers::getAsString() const {

clang/lib/Frontend/FrontendActions.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,10 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
467467
Entry.Event = BeginInstantiation ? "Begin" : "End";
468468
if (auto *NamedTemplate = dyn_cast_or_null<NamedDecl>(Inst.Entity)) {
469469
llvm::raw_string_ostream OS(Entry.Name);
470-
NamedTemplate->getNameForDiagnostic(OS, TheSema.getLangOpts(), true);
470+
PrintingPolicy Policy = TheSema.Context.getPrintingPolicy();
471+
// FIXME: Also ask for FullyQualifiedNames?
472+
Policy.SuppressDefaultTemplateArgs = false;
473+
NamedTemplate->getNameForDiagnostic(OS, Policy, true);
471474
const PresumedLoc DefLoc =
472475
TheSema.getSourceManager().getPresumedLoc(Inst.Entity->getLocation());
473476
if(!DefLoc.isInvalid())

clang/test/Misc/diag-template.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %clang_cc1 -verify %s
2+
3+
namespace default_args {
4+
template<typename T> struct char_traits;
5+
template<typename T> struct allocator;
6+
template<typename T, typename = char_traits<T>, typename = allocator<T>> struct basic_string {};
7+
8+
typedef basic_string<char> string;
9+
10+
template<typename T> T f(T);
11+
12+
void test1() {
13+
string s;
14+
f(s).size(); // expected-error {{no member named 'size' in 'default_args::basic_string<char>'}}
15+
}
16+
17+
template<typename T> struct default_delete {};
18+
template<class T, class Deleter = default_delete<T>> class unique_ptr {};
19+
template<class T, class Deleter> class unique_ptr<T[], Deleter> {};
20+
void test2() {
21+
unique_ptr<string> ups;
22+
f(ups).reset(); // expected-error {{no member named 'reset' in 'default_args::unique_ptr<default_args::basic_string<char>>'}}
23+
}
24+
25+
template<int A, int B = A> struct Z { int error[B]; }; // expected-error {{negative size}}
26+
Z<-1> z; // expected-note {{in instantiation of template class 'default_args::Z<-1>' requested here}}
27+
28+
template<template<typename> class A = allocator, template<typename> class B = A> struct Q {};
29+
void test3() {
30+
f(Q<>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<>'}}
31+
f(Q<allocator>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<>'}}
32+
f(Q<allocator, allocator>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<>'}}
33+
f(Q<char_traits>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<char_traits>'}}
34+
f(Q<char_traits, char_traits>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<char_traits>'}}
35+
f(Q<char_traits, allocator>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<char_traits, allocator>'}}
36+
}
37+
}

clang/test/SemaCXX/cxx14-compat.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace [[]] NS_with_attr {} // expected-warning {{incompatible with C++ stand
1616
enum { e [[]] }; // expected-warning {{incompatible with C++ standards before C++17}}
1717

1818
template<typename T = int> struct X {};
19-
X x; // expected-warning {{class template argument deduction is incompatible with C++ standards before C++17; for compatibility, use explicit type name 'X<int>'}}
19+
X x; // expected-warning {{class template argument deduction is incompatible with C++ standards before C++17; for compatibility, use explicit type name 'X<>'}}
2020

2121
template<template<typename> class> struct Y {};
2222
Y<X> yx; // ok, not class template argument deduction

clang/test/SemaCXX/generic-selection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ static_assert(A<int>::id == 1, "fail");
1414
static_assert(A<float>::id == 2, "fail");
1515
static_assert(A<double, double>::id == 3, "fail");
1616

17-
A<char> a1; // expected-note {{in instantiation of template class 'A<char, void *>' requested here}}
17+
A<char> a1; // expected-note {{in instantiation of template class 'A<char>' requested here}}
1818
A<short, int> a2; // expected-note {{in instantiation of template class 'A<short, int>' requested here}}
1919

2020
template <typename T, typename U>

0 commit comments

Comments
 (0)