Author: Richard Smith Date: 2020-11-11T15:05:51-08:00 New Revision: e7f3e2103cdb567dda4fd52f81bf4bc07179f5a8
URL: https://github.com/llvm/llvm-project/commit/e7f3e2103cdb567dda4fd52f81bf4bc07179f5a8 DIFF: https://github.com/llvm/llvm-project/commit/e7f3e2103cdb567dda4fd52f81bf4bc07179f5a8.diff LOG: 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. Added: clang/test/Misc/diag-template.cpp Modified: clang/include/clang/AST/PrettyPrinter.h clang/include/clang/AST/Type.h clang/lib/AST/DeclTemplate.cpp clang/lib/AST/TypePrinter.cpp clang/lib/Frontend/FrontendActions.cpp clang/test/SemaCXX/cxx14-compat.cpp clang/test/SemaCXX/generic-selection.cpp clang/test/SemaTemplate/class-template-id.cpp clang/test/SemaTemplate/class-template-spec.cpp clang/test/SemaTemplate/instantiation-default-1.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index dfd5851bb30d..50e2142e2ef0 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -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), @@ -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; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 1442bc740620..6dedd097ff89 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -61,6 +61,7 @@ class ExtQuals; class QualType; class ConceptDecl; class TagDecl; +class TemplateParameterList; class Type; enum { @@ -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 diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 9918377070c3..328ceaa63df3 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -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()); } } @@ -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()); } } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 5e9b22664185..3368905007a4 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -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()) { @@ -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()) @@ -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); @@ -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); } @@ -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 << '<'; @@ -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; @@ -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 { diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 0993e5eb033f..f38da54cebcc 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -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()) diff --git a/clang/test/Misc/diag-template.cpp b/clang/test/Misc/diag-template.cpp new file mode 100644 index 000000000000..30d4829409bb --- /dev/null +++ b/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>'}} + } +} diff --git a/clang/test/SemaCXX/cxx14-compat.cpp b/clang/test/SemaCXX/cxx14-compat.cpp index d70f477cd09a..d6396a7de4df 100644 --- a/clang/test/SemaCXX/cxx14-compat.cpp +++ b/clang/test/SemaCXX/cxx14-compat.cpp @@ -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 diff --git a/clang/test/SemaCXX/generic-selection.cpp b/clang/test/SemaCXX/generic-selection.cpp index c0a5d89fff67..8bf4a784f9bf 100644 --- a/clang/test/SemaCXX/generic-selection.cpp +++ b/clang/test/SemaCXX/generic-selection.cpp @@ -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> diff --git a/clang/test/SemaTemplate/class-template-id.cpp b/clang/test/SemaTemplate/class-template-id.cpp index 50cb3ef59ea4..b32a03e478d9 100644 --- a/clang/test/SemaTemplate/class-template-id.cpp +++ b/clang/test/SemaTemplate/class-template-id.cpp @@ -9,9 +9,9 @@ A<int, FLOAT> *foo(A<int> *ptr, A<int> const *ptr2, A<int, double> *ptr3) { if (ptr) return ptr; // okay else if (ptr2) - return ptr2; // expected-error{{cannot initialize return object of type 'A<int, FLOAT> *' (aka 'A<int, float> *') with an lvalue of type 'const A<int> *'}} + return ptr2; // expected-error{{cannot initialize return object of type 'A<int> *' with an lvalue of type 'const A<int> *'}} else { - return ptr3; // expected-error{{cannot initialize return object of type 'A<int, FLOAT> *' (aka 'A<int, float> *') with an lvalue of type 'A<int, double> *'}} + return ptr3; // expected-error{{cannot initialize return object of type 'A<int> *' with an lvalue of type 'A<int, double> *'}} } } diff --git a/clang/test/SemaTemplate/class-template-spec.cpp b/clang/test/SemaTemplate/class-template-spec.cpp index d763944371cc..e96ef44b7a25 100644 --- a/clang/test/SemaTemplate/class-template-spec.cpp +++ b/clang/test/SemaTemplate/class-template-spec.cpp @@ -22,7 +22,7 @@ int test_incomplete_specs(A<double, double> *a1, A<double> *a2) { (void)a1->x; // expected-error{{member access into incomplete type}} - (void)a2->x; // expected-error{{implicit instantiation of undefined template 'A<double, int>'}} + (void)a2->x; // expected-error{{implicit instantiation of undefined template 'A<double>'}} } typedef float FLOAT; diff --git a/clang/test/SemaTemplate/instantiation-default-1.cpp b/clang/test/SemaTemplate/instantiation-default-1.cpp index ab9eca75e239..33e01b6b8aa5 100644 --- a/clang/test/SemaTemplate/instantiation-default-1.cpp +++ b/clang/test/SemaTemplate/instantiation-default-1.cpp @@ -36,7 +36,7 @@ typedef int& int_ref_t; Def2<int_ref_t> *d2; // expected-note{{in instantiation of default argument for 'Def2<int &>' required here}} -template<> struct Def1<const int> { }; // expected-error{{redefinition of 'Def1<const int, const int>'}} +template<> struct Def1<const int> { }; // expected-error{{redefinition of 'Def1<const int>'}} template<typename T, typename T2 = T&> struct Def3; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits