Index: include/clang/AST/NestedNameSpecifier.h =================================================================== --- include/clang/AST/NestedNameSpecifier.h +++ include/clang/AST/NestedNameSpecifier.h @@ -212,12 +212,8 @@ /// parameter pack (for C++11 variadic templates). bool containsUnexpandedParameterPack() const; - /// Print this nested name specifier to the given output stream. If - /// `ResolveTemplateArguments` is true, we'll print actual types, e.g. - /// `ns::SomeTemplate` instead of - /// `ns::SomeTemplate`. - void print(raw_ostream &OS, const PrintingPolicy &Policy, - bool ResolveTemplateArguments = false) const; + /// Print this nested name specifier to the given output stream. + void print(raw_ostream &OS, const PrintingPolicy &Policy) const; void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(Prefix.getOpaqueValue()); Index: lib/AST/NestedNameSpecifier.cpp =================================================================== --- lib/AST/NestedNameSpecifier.cpp +++ lib/AST/NestedNameSpecifier.cpp @@ -271,8 +271,7 @@ /// Print this nested name specifier to the given output /// stream. -void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy, - bool ResolveTemplateArguments) const { +void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy) const { if (getPrefix()) getPrefix()->print(OS, Policy); @@ -305,15 +304,6 @@ LLVM_FALLTHROUGH; case TypeSpec: { - const auto *Record = - dyn_cast_or_null(getAsRecordDecl()); - if (ResolveTemplateArguments && Record) { - // Print the type trait with resolved template parameters. - Record->printName(OS); - printTemplateArgumentList(OS, Record->getTemplateArgs().asArray(), - Policy); - break; - } const Type *T = getAsType(); PrintingPolicy InnerPolicy(Policy); Index: lib/AST/TypePrinter.cpp =================================================================== --- lib/AST/TypePrinter.cpp +++ lib/AST/TypePrinter.cpp @@ -164,9 +164,35 @@ static SplitQualType splitAccordingToPolicy(QualType QT, const PrintingPolicy &Policy) { - if (Policy.PrintCanonicalTypes) - QT = QT.getCanonicalType(); - return QT.split(); + if (!Policy.PrintCanonicalTypes) + return QT.split(); + if (QT.isNull()) + return QT.split(); + // We do not canonicalize a few type classes because this would remove the + // information about whether a template parameter came from a default + // argument. + // In the following comments, consider: + // `template struct X {};`, and + // `struct Some { using Class = float; };` + switch (QT.getTypePtr()->getTypeClass()) { + // `X` gets canonicalized to `X`. We disable + // canonicalization so that `printTag()` can see the template parameters as + // written. + case Type::TemplateSpecialization: + // `X*` gets canonicalized to `X*`. Disabling + // canonicalization of the pointer/array will canonicalize the pointee when + // it is is processed, resulting in `X*`. + case Type::Pointer: + case Type::LValueReference: + case Type::RValueReference: + case Type::ConstantArray: + case Type::IncompleteArray: + case Type::VariableArray: + case Type::DependentSizedArray: + return QT.split(); + default: + return QT.getCanonicalType().split(); + } } void TypePrinter::print(QualType t, raw_ostream &OS, StringRef PlaceHolder) { Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -3052,40 +3052,6 @@ return Cond; } -namespace { - -// A PrinterHelper that prints more helpful diagnostics for some sub-expressions -// within failing boolean expression, such as substituting template parameters -// for actual types. -class FailedBooleanConditionPrinterHelper : public PrinterHelper { -public: - explicit FailedBooleanConditionPrinterHelper(const PrintingPolicy &P) - : Policy(P) {} - - bool handledStmt(Stmt *E, raw_ostream &OS) override { - const auto *DR = dyn_cast(E); - if (DR && DR->getQualifier()) { - // If this is a qualified name, expand the template arguments in nested - // qualifiers. - DR->getQualifier()->print(OS, Policy, true); - // Then print the decl itself. - const ValueDecl *VD = DR->getDecl(); - OS << VD->getName(); - if (const auto *IV = dyn_cast(VD)) { - // This is a template variable, print the expanded template arguments. - printTemplateArgumentList(OS, IV->getTemplateArgs().asArray(), Policy); - } - return true; - } - return false; - } - -private: - const PrintingPolicy Policy; -}; - -} // end anonymous namespace - std::pair Sema::findFailedBooleanCondition(Expr *Cond) { Cond = lookThroughRangesV3Condition(PP, Cond); @@ -3124,8 +3090,7 @@ llvm::raw_string_ostream Out(Description); PrintingPolicy Policy = getPrintingPolicy(); Policy.PrintCanonicalTypes = true; - FailedBooleanConditionPrinterHelper Helper(Policy); - FailedCond->printPretty(Out, &Helper, Policy, 0, "\n", nullptr); + FailedCond->printPretty(Out, nullptr, Policy, 0, "\n", nullptr); } return { FailedCond, Description }; } Index: test/SemaCXX/static-assert-cxx17.cpp =================================================================== --- test/SemaCXX/static-assert-cxx17.cpp +++ test/SemaCXX/static-assert-cxx17.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1z -triple=x86_64-linux-gnu -template +template struct S1 { static constexpr const bool value = false; }; @@ -14,7 +14,7 @@ static inline constexpr bool var = global_inline_var; }; -template +template inline constexpr bool constexpr_return_false() { return false; } @@ -23,6 +23,8 @@ void foo() { static_assert(S1::value); // expected-error@-1{{static_assert failed due to requirement 'S1::value'}} + static_assert(S1::value); + // expected-error@-1{{static_assert failed due to requirement 'S1::value'}} } template void foo(); // expected-note@-1{{in instantiation of function template specialization 'foo' requested here}} @@ -66,7 +68,7 @@ using U = float; }; -template +template struct X { int i = 0; int j = 0; @@ -74,7 +76,8 @@ }; template -void foo6() { +void test_template_parameter_from_default() { + X x; static_assert(X()); // expected-error@-1{{static_assert failed due to requirement 'X()'}} static_assert(X{}); @@ -93,12 +96,60 @@ // expected-error@-1{{static_assert failed due to requirement '(const X *)nullptr'}} static_assert(static_cast *>(nullptr)); // expected-error@-1{{static_assert failed due to requirement 'static_cast *>(nullptr)'}} + static_assert(static_cast &>(x)); + // expected-error@-1{{static_assert failed due to requirement 'static_cast &>(x)'}} + static_assert(static_cast &&>(x)); + // expected-error@-1{{static_assert failed due to requirement 'static_cast &&>(x)'}} static_assert((const X[]){} == nullptr); - // expected-error@-1{{static_assert failed due to requirement '(X const[0]){} == nullptr'}} + // expected-error@-1{{static_assert failed due to requirement '(const X [0]){} == nullptr'}} static_assert(sizeof(X().X::~X())>) == 0); // expected-error@-1{{static_assert failed due to requirement 'sizeof(X) == 0'}} + static_assert(constexpr_return_false()); + // expected-error@-1{{static_assert failed due to requirement 'constexpr_return_false()'}} +} +template void test_template_parameter_from_default(); +// expected-note@-1{{in instantiation of function template specialization 'test_template_parameter_from_default' requested here}} + +template +void test_template_parameter_as_written() { + static_assert(X()); + // expected-error@-1{{static_assert failed due to requirement 'X()'}} + static_assert(sizeof(X) == 0); + // expected-error@-1{{static_assert failed due to requirement 'sizeof(X) == 0'}} static_assert(constexpr_return_false()); // expected-error@-1{{static_assert failed due to requirement 'constexpr_return_false()'}} } -template void foo6(); -// expected-note@-1{{in instantiation of function template specialization 'foo6' requested here}} +template void test_template_parameter_as_written(); +// expected-note@-1{{in instantiation of function template specialization 'test_template_parameter_as_written' requested here}} + + +template +struct is_pointerable { static constexpr bool value = false; }; + +template +struct is_pointerable { static constexpr bool value = true; }; + +template +void test_is_pointerable() +{ + static_assert(is_pointerable::value); + // expected-error@-1{{due to requirement 'is_pointerable::value'}} + static_assert(not is_pointerable::value); + // expected-error@-1{{due to requirement '!is_pointerable::value'}} +} +template void test_is_pointerable(); +// expected-note@-1{{in instantiation of function template specialization 'test_is_pointerable' requested here}} +template void test_is_pointerable(); +// expected-note@-1{{in instantiation of function template specialization 'test_is_pointerable' requested here}} + + +// This test emulates std::vector with a default allocator. +// FIXME: Ideally we would like to avoid printing the default here (print 'S1 *, void>::value') +template struct A {}; +template> struct V {}; +template void test_std_vector_like_from_default() { + static_assert(S1::value); + // expected-error@-1{{due to requirement 'S1 > *, void>::value'}} +} +template void test_std_vector_like_from_default>(); +// expected-note@-1{{in instantiation of function template specialization 'test_std_vector_like_from_default > >' requested here}} Index: test/SemaCXX/static-assert.cpp =================================================================== --- test/SemaCXX/static-assert.cpp +++ test/SemaCXX/static-assert.cpp @@ -127,7 +127,7 @@ static_assert(!(std::is_const()()), "message"); // expected-error@-1{{static_assert failed due to requirement '!(std::is_const()())' "message"}} static_assert(std::is_same()), int>::value, "message"); -// expected-error@-1{{static_assert failed due to requirement 'std::is_same, int>::value' "message"}} +// expected-error@-1{{static_assert failed due to requirement 'std::is_same, int>::value' "message"}} static_assert(std::is_const::value, "message"); // expected-error@-1{{static_assert failed due to requirement 'std::is_const::value' "message"}} static_assert(std::is_const::value, "message");