Skip to content

Commit

Permalink
Suppress printing template arguments that match default template
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
zygoloid committed Nov 11, 2020
1 parent 686d8a0 commit e7f3e21
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 27 deletions.
7 changes: 6 additions & 1 deletion clang/include/clang/AST/PrettyPrinter.h
Expand Up @@ -55,7 +55,8 @@ struct PrintingPolicy {
SuppressInitializers(false), ConstantArraySizeAsWritten(false),
AnonymousTagLocations(true), SuppressStrongLifetime(false),
SuppressLifetimeQualifiers(false),
SuppressTemplateArgsInCXXConstructors(false), Bool(LO.Bool),
SuppressTemplateArgsInCXXConstructors(false),
SuppressDefaultTemplateArgs(true), Bool(LO.Bool),
Nullptr(LO.CPlusPlus11), Restrict(LO.C99), Alignof(LO.CPlusPlus11),
UnderscoreAlignof(LO.C11), UseVoidForZeroParams(!LO.CPlusPlus),
SplitTemplateClosers(!LO.CPlusPlus11), TerseOutput(false),
Expand Down Expand Up @@ -167,6 +168,10 @@ struct PrintingPolicy {
/// constructors.
unsigned SuppressTemplateArgsInCXXConstructors : 1;

/// When true, attempt to suppress template arguments that match the default
/// argument for the parameter.
unsigned SuppressDefaultTemplateArgs : 1;

/// Whether we can use 'bool' rather than '_Bool' (even if the language
/// doesn't actually have 'bool', because, e.g., it is defined as a macro).
unsigned Bool : 1;
Expand Down
10 changes: 7 additions & 3 deletions clang/include/clang/AST/Type.h
Expand Up @@ -61,6 +61,7 @@ class ExtQuals;
class QualType;
class ConceptDecl;
class TagDecl;
class TemplateParameterList;
class Type;

enum {
Expand Down Expand Up @@ -5196,15 +5197,18 @@ class alignas(8) TemplateSpecializationType
/// enclosing the template arguments.
void printTemplateArgumentList(raw_ostream &OS,
ArrayRef<TemplateArgument> Args,
const PrintingPolicy &Policy);
const PrintingPolicy &Policy,
const TemplateParameterList *TPL = nullptr);

void printTemplateArgumentList(raw_ostream &OS,
ArrayRef<TemplateArgumentLoc> Args,
const PrintingPolicy &Policy);
const PrintingPolicy &Policy,
const TemplateParameterList *TPL = nullptr);

void printTemplateArgumentList(raw_ostream &OS,
const TemplateArgumentListInfo &Args,
const PrintingPolicy &Policy);
const PrintingPolicy &Policy,
const TemplateParameterList *TPL = nullptr);

/// The injected class name of a C++ class template or class
/// template partial specialization. Used to record that a type was
Expand Down
16 changes: 12 additions & 4 deletions clang/lib/AST/DeclTemplate.cpp
Expand Up @@ -914,10 +914,14 @@ void ClassTemplateSpecializationDecl::getNameForDiagnostic(
const auto *PS = dyn_cast<ClassTemplatePartialSpecializationDecl>(this);
if (const ASTTemplateArgumentListInfo *ArgsAsWritten =
PS ? PS->getTemplateArgsAsWritten() : nullptr) {
printTemplateArgumentList(OS, ArgsAsWritten->arguments(), Policy);
printTemplateArgumentList(
OS, ArgsAsWritten->arguments(), Policy,
getSpecializedTemplate()->getTemplateParameters());
} else {
const TemplateArgumentList &TemplateArgs = getTemplateArgs();
printTemplateArgumentList(OS, TemplateArgs.asArray(), Policy);
printTemplateArgumentList(
OS, TemplateArgs.asArray(), Policy,
getSpecializedTemplate()->getTemplateParameters());
}
}

Expand Down Expand Up @@ -1261,10 +1265,14 @@ void VarTemplateSpecializationDecl::getNameForDiagnostic(
const auto *PS = dyn_cast<VarTemplatePartialSpecializationDecl>(this);
if (const ASTTemplateArgumentListInfo *ArgsAsWritten =
PS ? PS->getTemplateArgsAsWritten() : nullptr) {
printTemplateArgumentList(OS, ArgsAsWritten->arguments(), Policy);
printTemplateArgumentList(
OS, ArgsAsWritten->arguments(), Policy,
getSpecializedTemplate()->getTemplateParameters());
} else {
const TemplateArgumentList &TemplateArgs = getTemplateArgs();
printTemplateArgumentList(OS, TemplateArgs.asArray(), Policy);
printTemplateArgumentList(
OS, TemplateArgs.asArray(), Policy,
getSpecializedTemplate()->getTemplateParameters());
}
}

Expand Down
187 changes: 175 additions & 12 deletions clang/lib/AST/TypePrinter.cpp
Expand Up @@ -1124,7 +1124,9 @@ void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) {
OS << T->getTypeConstraintConcept()->getName();
auto Args = T->getTypeConstraintArguments();
if (!Args.empty())
printTemplateArgumentList(OS, Args, Policy);
printTemplateArgumentList(
OS, Args, Policy,
T->getTypeConstraintConcept()->getTemplateParameters());
OS << ' ';
}
switch (T->getKeyword()) {
Expand Down Expand Up @@ -1226,7 +1228,9 @@ void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) {
IncludeStrongLifetimeRAII Strong(Policy);
OS << Spec->getIdentifier()->getName();
const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
printTemplateArgumentList(OS, TemplateArgs.asArray(), Policy);
printTemplateArgumentList(
OS, TemplateArgs.asArray(), Policy,
Spec->getSpecializedTemplate()->getTemplateParameters());
OS << "::";
} else if (const auto *Tag = dyn_cast<TagDecl>(DC)) {
if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl())
Expand Down Expand Up @@ -1317,7 +1321,9 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) {
Args = TemplateArgs.asArray();
}
IncludeStrongLifetimeRAII Strong(Policy);
printTemplateArgumentList(OS, Args, Policy);
printTemplateArgumentList(
OS, Args, Policy,
Spec->getSpecializedTemplate()->getTemplateParameters());
}

spaceBeforePlaceHolder(OS);
Expand Down Expand Up @@ -1389,7 +1395,11 @@ void TypePrinter::printTemplateSpecializationBefore(
IncludeStrongLifetimeRAII Strong(Policy);
T->getTemplateName().print(OS, Policy);

printTemplateArgumentList(OS, T->template_arguments(), Policy);
const TemplateParameterList *TPL = nullptr;
if (TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl())
TPL = TD->getTemplateParameters();

printTemplateArgumentList(OS, T->template_arguments(), Policy, TPL);
spaceBeforePlaceHolder(OS);
}

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

static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg,
TemplateArgument Pattern,
ArrayRef<TemplateArgument> Args,
unsigned Depth);

static bool isSubstitutedType(ASTContext &Ctx, QualType T, QualType Pattern,
ArrayRef<TemplateArgument> Args, unsigned Depth) {
if (Ctx.hasSameType(T, Pattern))
return true;

// A type parameter matches its argument.
if (auto *TTPT = Pattern->getAs<TemplateTypeParmType>()) {
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<TemplateSpecializationType>()) {
TemplateName Template;
ArrayRef<TemplateArgument> TemplateArgs;
if (auto *TTST = T->getAs<TemplateSpecializationType>()) {
Template = TTST->getTemplateName();
TemplateArgs = TTST->template_arguments();
} else if (auto *CTSD = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
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->getNumArgs())
return false;
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
if (!isSubstitutedTemplateArgument(Ctx, TemplateArgs[I], PTST->getArg(I),
Args, Depth))
return false;
return true;
}

// FIXME: Handle more cases.
return false;
}

static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg,
TemplateArgument Pattern,
ArrayRef<TemplateArgument> 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<DeclRefExpr>(Pattern.getAsExpr()->IgnoreParenImpCasts())) {
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(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<TemplateTemplateParmDecl>(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<TemplateArgument> 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<TemplateTypeParmDecl>(Param)) {
return TTPD->hasDefaultArgument() &&
isSubstitutedTemplateArgument(Ctx, Arg, TTPD->getDefaultArgument(),
Args, Depth);
} else if (auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) {
return TTPD->hasDefaultArgument() &&
isSubstitutedTemplateArgument(
Ctx, Arg, TTPD->getDefaultArgument().getArgument(), Args, Depth);
} else if (auto *NTTPD = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
return NTTPD->hasDefaultArgument() &&
isSubstitutedTemplateArgument(Ctx, Arg, NTTPD->getDefaultArgument(),
Args, Depth);
}
return false;
}

template<typename TA>
static void printTo(raw_ostream &OS, ArrayRef<TA> Args,
const PrintingPolicy &Policy, bool SkipBrackets) {
const PrintingPolicy &Policy, bool SkipBrackets,
const TemplateParameterList *TPL) {
// Drop trailing template arguments that match default arguments.
if (TPL && Policy.SuppressDefaultTemplateArgs &&
!Policy.PrintCanonicalTypes && !Args.empty() &&
Args.size() <= TPL->size()) {
ASTContext &Ctx = TPL->getParam(0)->getASTContext();
llvm::SmallVector<TemplateArgument, 8> OrigArgs;
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()))
Args = Args.drop_back();
}

const char *Comma = Policy.MSVCFormatting ? "," : ", ";
if (!SkipBrackets)
OS << '<';
Expand All @@ -1806,7 +1966,7 @@ static void printTo(raw_ostream &OS, ArrayRef<TA> Args,
if (Argument.getKind() == TemplateArgument::Pack) {
if (Argument.pack_size() && !FirstArg)
OS << Comma;
printTo(ArgOS, Argument.getPackAsArray(), Policy, true);
printTo(ArgOS, Argument.getPackAsArray(), Policy, true, nullptr);
} else {
if (!FirstArg)
OS << Comma;
Expand Down Expand Up @@ -1839,20 +1999,23 @@ static void printTo(raw_ostream &OS, ArrayRef<TA> Args,

void clang::printTemplateArgumentList(raw_ostream &OS,
const TemplateArgumentListInfo &Args,
const PrintingPolicy &Policy) {
return printTo(OS, Args.arguments(), Policy, false);
const PrintingPolicy &Policy,
const TemplateParameterList *TPL) {
printTemplateArgumentList(OS, Args.arguments(), Policy, TPL);
}

void clang::printTemplateArgumentList(raw_ostream &OS,
ArrayRef<TemplateArgument> Args,
const PrintingPolicy &Policy) {
printTo(OS, Args, Policy, false);
const PrintingPolicy &Policy,
const TemplateParameterList *TPL) {
printTo(OS, Args, Policy, false, TPL);
}

void clang::printTemplateArgumentList(raw_ostream &OS,
ArrayRef<TemplateArgumentLoc> Args,
const PrintingPolicy &Policy) {
printTo(OS, Args, Policy, false);
const PrintingPolicy &Policy,
const TemplateParameterList *TPL) {
printTo(OS, Args, Policy, false, TPL);
}

std::string Qualifiers::getAsString() const {
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Frontend/FrontendActions.cpp
Expand Up @@ -467,7 +467,10 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
Entry.Event = BeginInstantiation ? "Begin" : "End";
if (auto *NamedTemplate = dyn_cast_or_null<NamedDecl>(Inst.Entity)) {
llvm::raw_string_ostream OS(Entry.Name);
NamedTemplate->getNameForDiagnostic(OS, TheSema.getLangOpts(), true);
PrintingPolicy Policy = TheSema.Context.getPrintingPolicy();
// FIXME: Also ask for FullyQualifiedNames?
Policy.SuppressDefaultTemplateArgs = false;
NamedTemplate->getNameForDiagnostic(OS, Policy, true);
const PresumedLoc DefLoc =
TheSema.getSourceManager().getPresumedLoc(Inst.Entity->getLocation());
if(!DefLoc.isInvalid())
Expand Down
37 changes: 37 additions & 0 deletions clang/test/Misc/diag-template.cpp
@@ -0,0 +1,37 @@
// RUN: %clang_cc1 -verify %s

namespace default_args {
template<typename T> struct char_traits;
template<typename T> struct allocator;
template<typename T, typename = char_traits<T>, typename = allocator<T>> struct basic_string {};

typedef basic_string<char> string;

template<typename T> T f(T);

void test1() {
string s;
f(s).size(); // expected-error {{no member named 'size' in 'default_args::basic_string<char>'}}
}

template<typename T> struct default_delete {};
template<class T, class Deleter = default_delete<T>> class unique_ptr {};
template<class T, class Deleter> class unique_ptr<T[], Deleter> {};
void test2() {
unique_ptr<string> ups;
f(ups).reset(); // expected-error {{no member named 'reset' in 'default_args::unique_ptr<default_args::basic_string<char>>'}}
}

template<int A, int B = A> struct Z { int error[B]; }; // expected-error {{negative size}}
Z<-1> z; // expected-note {{in instantiation of template class 'default_args::Z<-1>' requested here}}

template<template<typename> class A = allocator, template<typename> class B = A> struct Q {};
void test3() {
f(Q<>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<>'}}
f(Q<allocator>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<>'}}
f(Q<allocator, allocator>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<>'}}
f(Q<char_traits>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<char_traits>'}}
f(Q<char_traits, char_traits>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<char_traits>'}}
f(Q<char_traits, allocator>()).g(); // expected-error {{no member named 'g' in 'default_args::Q<char_traits, allocator>'}}
}
}
2 changes: 1 addition & 1 deletion clang/test/SemaCXX/cxx14-compat.cpp
Expand Up @@ -16,7 +16,7 @@ namespace [[]] NS_with_attr {} // expected-warning {{incompatible with C++ stand
enum { e [[]] }; // expected-warning {{incompatible with C++ standards before C++17}}

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

template<template<typename> class> struct Y {};
Y<X> yx; // ok, not class template argument deduction
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaCXX/generic-selection.cpp
Expand Up @@ -14,7 +14,7 @@ static_assert(A<int>::id == 1, "fail");
static_assert(A<float>::id == 2, "fail");
static_assert(A<double, double>::id == 3, "fail");

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

template <typename T, typename U>
Expand Down

0 comments on commit e7f3e21

Please sign in to comment.