Index: clang/include/clang/AST/StmtDataCollectors.td =================================================================== --- clang/include/clang/AST/StmtDataCollectors.td +++ clang/include/clang/AST/StmtDataCollectors.td @@ -51,7 +51,7 @@ // Print all template arguments into ArgString llvm::raw_string_ostream OS(ArgString); for (unsigned i = 0; i < Args->size(); ++i) { - Args->get(i).print(Context.getLangOpts(), OS); + Args->get(i).print(Context.getLangOpts(), OS, true); // Add a padding character so that 'foo()' != 'foo()'. OS << '\n'; } Index: clang/include/clang/AST/TemplateBase.h =================================================================== --- clang/include/clang/AST/TemplateBase.h +++ clang/include/clang/AST/TemplateBase.h @@ -378,7 +378,8 @@ TemplateArgument getPackExpansionPattern() const; /// Print this template argument to the given output stream. - void print(const PrintingPolicy &Policy, raw_ostream &Out) const; + void print(const PrintingPolicy &Policy, raw_ostream &Out, + bool knownType) const; /// Debugging aid that dumps the template argument. void dump(raw_ostream &Out) const; Index: clang/lib/AST/ASTTypeTraits.cpp =================================================================== --- clang/lib/AST/ASTTypeTraits.cpp +++ clang/lib/AST/ASTTypeTraits.cpp @@ -128,11 +128,15 @@ void DynTypedNode::print(llvm::raw_ostream &OS, const PrintingPolicy &PP) const { - if (const TemplateArgument *TA = get()) - TA->print(PP, OS); - else if (const TemplateArgumentLoc *TAL = get()) - TAL->getArgument().print(PP, OS); - else if (const TemplateName *TN = get()) + if (const TemplateArgument *TA = get()) { + bool knownType = true; + if (auto *DRE = dyn_cast(TA->getAsExpr())) + if (DRE->hadMultipleCandidates()) + knownType = false; + TA->print(PP, OS, knownType); + } else if (const TemplateArgumentLoc *TAL = get()) { + TAL->getArgument().print(PP, OS, true); + } else if (const TemplateName *TN = get()) TN->print(OS, PP); else if (const NestedNameSpecifier *NNS = get()) NNS->print(OS, PP); Index: clang/lib/AST/DeclPrinter.cpp =================================================================== --- clang/lib/AST/DeclPrinter.cpp +++ clang/lib/AST/DeclPrinter.cpp @@ -1077,7 +1077,7 @@ for (size_t I = 0, E = Args.size(); I < E; ++I) { if (I) Out << ", "; - Args[I].print(Policy, Out); + Args[I].print(Policy, Out, true); } Out << ">"; } @@ -1087,7 +1087,11 @@ for (size_t I = 0, E = Args.size(); I < E; ++I) { if (I) Out << ", "; - Args[I].getArgument().print(Policy, Out); + bool knownType = true; + if (auto *DRE = dyn_cast(Args[I].getArgument().getAsExpr())) + if (DRE->hadMultipleCandidates()) + knownType = false; + Args[I].getArgument().print(Policy, Out, knownType); } Out << ">"; } Index: clang/lib/AST/DeclTemplate.cpp =================================================================== --- clang/lib/AST/DeclTemplate.cpp +++ clang/lib/AST/DeclTemplate.cpp @@ -1426,8 +1426,13 @@ ConceptName.printName(OS, Policy); if (hasExplicitTemplateArgs()) { OS << "<"; - for (auto &ArgLoc : ArgsAsWritten->arguments()) - ArgLoc.getArgument().print(Policy, OS); + for (auto &ArgLoc : ArgsAsWritten->arguments()) { + bool knownType = true; + if (auto *DRE = dyn_cast(ArgLoc.getArgument().getAsExpr())) + if (DRE->hadMultipleCandidates()) + knownType = false; + ArgLoc.getArgument().print(Policy, OS, knownType); + } OS << ">"; } } Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -771,7 +771,7 @@ StringRef Param = Params->getParam(i)->getName(); if (Param.empty()) continue; TOut << Param << " = "; - Args.get(i).print(Policy, TOut); + Args.get(i).print(Policy, TOut, true); TOut << ", "; } } @@ -787,7 +787,7 @@ StringRef Param = Params->getParam(i)->getName(); if (Param.empty()) continue; TOut << Param << " = "; - Args->get(i).print(Policy, TOut); + Args->get(i).print(Policy, TOut, true); TOut << ", "; } } Index: clang/lib/AST/TemplateBase.cpp =================================================================== --- clang/lib/AST/TemplateBase.cpp +++ clang/lib/AST/TemplateBase.cpp @@ -50,8 +50,10 @@ /// \param Out the raw_ostream instance to use for printing. /// /// \param Policy the printing policy for EnumConstantDecl printing. -static void printIntegral(const TemplateArgument &TemplArg, - raw_ostream &Out, const PrintingPolicy& Policy) { +/// +/// \param knownType the flag to determine printing suffix/prefix type. +static void printIntegral(const TemplateArgument &TemplArg, raw_ostream &Out, + const PrintingPolicy &Policy, bool knownType) { const Type *T = TemplArg.getIntegralType().getTypePtr(); const llvm::APSInt &Val = TemplArg.getAsIntegral(); @@ -70,13 +72,52 @@ if (T->isBooleanType() && !Policy.MSVCFormatting) { Out << (Val.getBoolValue() ? "true" : "false"); + } else if (T->isBooleanType()) { + Out << Val; } else if (T->isCharType()) { const char Ch = Val.getZExtValue(); Out << ((Ch == '\'') ? "'\\" : "'"); Out.write_escaped(StringRef(&Ch, 1), /*UseHexEscapes=*/ true); Out << "'"; } else { - Out << Val; + if (knownType) { + if (auto *autoT = T->getAs()) { + if (autoT->getAs()) { + knownType = false; + } + } else if (T->getAs()) { + knownType = false; + } + } + if (!knownType) { + if (auto *BT = T->getAs()) { + switch (BT->getKind()) { + case BuiltinType::ULongLong: + Out << Val << "ULL"; + break; + case BuiltinType::LongLong: + Out << Val << "LL"; + break; + case BuiltinType::ULong: + Out << Val << "UL"; + break; + case BuiltinType::Long: + Out << Val << "L"; + break; + case BuiltinType::UInt: + Out << Val << "U"; + break; + case BuiltinType::Int: + Out << Val; + break; + default: + Out << "(" << T->getCanonicalTypeInternal().getAsString(Policy) << ")" + << Val; + break; + } + } + } else + Out << Val; } } @@ -336,8 +377,9 @@ llvm_unreachable("Invalid TemplateArgument Kind!"); } -void TemplateArgument::print(const PrintingPolicy &Policy, - raw_ostream &Out) const { +void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out, + bool knownType) const { + switch (getKind()) { case Null: Out << "(no value)"; @@ -372,7 +414,7 @@ break; case Integral: - printIntegral(*this, Out, Policy); + printIntegral(*this, Out, Policy, knownType); break; case Expression: @@ -388,7 +430,7 @@ else Out << ", "; - P.print(Policy, Out); + P.print(Policy, Out, knownType); } Out << ">"; break; @@ -399,7 +441,7 @@ LangOptions LO; // FIXME! see also TemplateName::dump(). LO.CPlusPlus = true; LO.Bool = true; - print(PrintingPolicy(LO), Out); + print(PrintingPolicy(LO), Out, false); } LLVM_DUMP_METHOD void TemplateArgument::dump() const { dump(llvm::errs()); } @@ -494,7 +536,7 @@ LangOptions LangOpts; LangOpts.CPlusPlus = true; PrintingPolicy Policy(LangOpts); - Arg.print(Policy, OS); + Arg.print(Policy, OS, true); return DB << OS.str(); } } Index: clang/lib/AST/TypePrinter.cpp =================================================================== --- clang/lib/AST/TypePrinter.cpp +++ clang/lib/AST/TypePrinter.cpp @@ -1778,7 +1778,7 @@ static void printArgument(const TemplateArgument &A, const PrintingPolicy &PP, llvm::raw_ostream &OS) { - A.print(PP, OS); + A.print(PP, OS, true); } static void printArgument(const TemplateArgumentLoc &A, @@ -1786,7 +1786,11 @@ const TemplateArgument::ArgKind &Kind = A.getArgument().getKind(); if (Kind == TemplateArgument::ArgKind::Type) return A.getTypeSourceInfo()->getType().print(OS, PP); - return A.getArgument().print(PP, OS); + bool knownType = true; + if (auto *DRE = dyn_cast(A.getArgument().getAsExpr())) + if (DRE->hadMultipleCandidates()) + knownType = false; + return A.getArgument().print(PP, OS, knownType); } template Index: clang/lib/Analysis/PathDiagnostic.cpp =================================================================== --- clang/lib/Analysis/PathDiagnostic.cpp +++ clang/lib/Analysis/PathDiagnostic.cpp @@ -898,7 +898,7 @@ if (TArg.getKind() == TemplateArgument::ArgKind::Pack) { describeTemplateParameters(Out, TArg.getPackAsArray(), LO); } else { - TArg.print(PrintingPolicy(LO), Out); + TArg.print(PrintingPolicy(LO), Out, true); } } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -963,7 +963,11 @@ for (auto &Arg : Args.arguments()) { if (!First) OS << ", "; - Arg.getArgument().print(PrintingPolicy, OS); + bool knownType = true; + if (auto *DRE = dyn_cast(Arg.getArgument().getAsExpr())) + if (DRE->hadMultipleCandidates()) + knownType = false; + Arg.getArgument().print(PrintingPolicy, OS, knownType); First = false; } return std::string(OS.str()); Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -10769,7 +10769,7 @@ } Out << " = "; - Args[I].print(getPrintingPolicy(), Out); + Args[I].print(getPrintingPolicy(), Out, true); } Out << ']'; Index: clang/lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- clang/lib/Sema/SemaTemplateDeduction.cpp +++ clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4694,8 +4694,14 @@ OS << "'" << Concept->getName(); if (TypeLoc.hasExplicitTemplateArgs()) { OS << "<"; - for (const auto &Arg : Type.getTypeConstraintArguments()) - Arg.print(S.getPrintingPolicy(), OS); + for (const auto &Arg : Type.getTypeConstraintArguments()) { + bool knownType = true; + if (auto *DRE = dyn_cast(Arg.getAsExpr())) { + if (DRE->hadMultipleCandidates()) + knownType = false; + } + Arg.print(S.getPrintingPolicy(), OS, knownType); + } OS << ">"; } OS << "'"; Index: clang/test/SemaTemplate/temp_arg_nontype.cpp =================================================================== --- clang/test/SemaTemplate/temp_arg_nontype.cpp +++ clang/test/SemaTemplate/temp_arg_nontype.cpp @@ -270,6 +270,23 @@ void test_char_possibly_negative() { enable_if_char<'\x02'>::type i; } // expected-error{{enable_if_char<'\x02'>'; did you mean 'enable_if_char<'a'>::type'?}} void test_char_single_quote() { enable_if_char<'\''>::type i; } // expected-error{{enable_if_char<'\''>'; did you mean 'enable_if_char<'a'>::type'?}} void test_char_backslash() { enable_if_char<'\\'>::type i; } // expected-error{{enable_if_char<'\\'>'; did you mean 'enable_if_char<'a'>::type'?}} + + template struct enable_if_int {}; + template <> struct enable_if_int<1> { typedef int type; }; // expected-note{{'enable_if_int<1>::type' declared here}} + void test_int() { enable_if_int<2>::type i; } // expected-error{{enable_if_int<2>'; did you mean 'enable_if_int<1>::type'?}} + + template struct enable_if_unsigned_int {}; + template <> struct enable_if_unsigned_int<1> { typedef int type; }; // expected-note{{'enable_if_unsigned_int<1>::type' declared here}} + void test_unsigned_int() { enable_if_unsigned_int<2>::type i; } // expected-error{{enable_if_unsigned_int<2>'; did you mean 'enable_if_unsigned_int<1>::type'?}} + + template struct enable_if_unsigned_long_long {}; + template <> struct enable_if_unsigned_long_long<1> { typedef int type; }; // expected-note{{'enable_if_unsigned_long_long<1>::type' declared here}} + void test_unsigned_long_long() { enable_if_unsigned_long_long<2>::type i; } // expected-error{{enable_if_unsigned_long_long<2>'; did you mean 'enable_if_unsigned_long_long<1>::type'?}} + + template struct enable_if_long_long {}; + template <> struct enable_if_long_long<1> { typedef int type; }; // expected-note{{'enable_if_long_long<1>::type' declared here}} + void test_long_long() { enable_if_long_long<2>::type i; } // expected-error{{enable_if_long_long<2>'; did you mean 'enable_if_long_long<1>::type'?}} + } namespace PR10579 { Index: clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp =================================================================== --- clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -459,3 +459,13 @@ X y; int n = y.call(); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void *'}} } + +namespace PR9227 { + template struct S {}; + template <> struct S<1> { using type = int; }; // expected-note {{'S<1>::type' declared here}} + S<1L>::type t; // expected-error {{no type named 'type' in 'PR9227::S<1L>'; did you mean 'S<1>::type'?}} + + template struct A {}; + template <> struct A<1> { using type = int; }; // expected-note {{'A<1>::type' declared here}} + A<2>::type x; // expected-error {{no type named 'type' in 'PR9227::A<2>'; did you mean 'A<1>::type'?}} +} Index: clang/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp =================================================================== --- clang/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp +++ clang/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp @@ -20,7 +20,11 @@ llvm::raw_string_ostream Stream(ArgStr); const TemplateArgument &Arg = ArgLoc.getArgument(); - Arg.print(Context->getPrintingPolicy(), Stream); + bool knownType = true; + if (auto *DRE = dyn_cast(Arg.getAsExpr())) + if (DRE->hadMultipleCandidates()) + knownType = false; + Arg.print(Context->getPrintingPolicy(), Stream, knownType); Match(Stream.str(), ArgLoc.getLocation()); return ExpectedLocationVisitor:: TraverseTemplateArgumentLoc(ArgLoc);