courbet updated this revision to Diff 180035.
courbet marked 2 inline comments as done.
courbet added a comment.

Add more tests as suggested in the comments.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D55933/new/

https://reviews.llvm.org/D55933

Files:
  include/clang/AST/NestedNameSpecifier.h
  lib/AST/NestedNameSpecifier.cpp
  lib/AST/TypePrinter.cpp
  lib/Sema/SemaTemplate.cpp
  test/SemaCXX/static-assert-cxx17.cpp
  test/SemaCXX/static-assert.cpp

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<const ExampleTypes::T>()()), "message");
 // expected-error@-1{{static_assert failed due to requirement '!(std::is_const<const int>()())' "message"}}
 static_assert(std::is_same<decltype(std::is_const<const ExampleTypes::T>()), int>::value, "message");
-// expected-error@-1{{static_assert failed due to requirement 'std::is_same<std::is_const<const int>, int>::value' "message"}}
+// expected-error@-1{{static_assert failed due to requirement 'std::is_same<is_const<const int>, int>::value' "message"}}
 static_assert(std::is_const<decltype(ExampleTypes::T(3))>::value, "message");
 // expected-error@-1{{static_assert failed due to requirement 'std::is_const<int>::value' "message"}}
 static_assert(std::is_const<decltype(ExampleTypes::T())>::value, "message");
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 <typename U, typename V>
+template <typename U, typename V = U>
 struct S1 {
   static constexpr const bool value = false;
 };
@@ -14,7 +14,7 @@
   static inline constexpr bool var = global_inline_var<U, V>;
 };
 
-template <typename U, typename V>
+template <typename U, typename V = double>
 inline constexpr bool constexpr_return_false() {
   return false;
 }
@@ -23,6 +23,8 @@
 void foo() {
   static_assert(S1<U, V>::value);
   // expected-error@-1{{static_assert failed due to requirement 'S1<int, float>::value'}}
+  static_assert(S1<U>::value);
+  // expected-error@-1{{static_assert failed due to requirement 'S1<int>::value'}}
 }
 template void foo<int, float>();
 // expected-note@-1{{in instantiation of function template specialization 'foo<int, float>' requested here}}
@@ -66,7 +68,7 @@
   using U = float;
 };
 
-template <class T>
+template <class T, class U = double>
 struct X {
   int i = 0;
   int j = 0;
@@ -74,7 +76,7 @@
 };
 
 template <class T>
-void foo6() {
+void test_template_parameter_from_default() {
   static_assert(X<typename T::T>());
   // expected-error@-1{{static_assert failed due to requirement 'X<int>()'}}
   static_assert(X<typename T::T>{});
@@ -94,11 +96,55 @@
   static_assert(static_cast<const X<typename T::T> *>(nullptr));
   // expected-error@-1{{static_assert failed due to requirement 'static_cast<const X<int> *>(nullptr)'}}
   static_assert((const X<typename T::T>[]){} == nullptr);
-  // expected-error@-1{{static_assert failed due to requirement '(X<int> const[0]){} == nullptr'}}
+  // expected-error@-1{{static_assert failed due to requirement '(const X<int> [0]){} == nullptr'}}
   static_assert(sizeof(X<decltype(X<typename T::T>().X<typename T::T>::~X())>) == 0);
   // expected-error@-1{{static_assert failed due to requirement 'sizeof(X<void>) == 0'}}
+  static_assert(constexpr_return_false<typename T::T>());
+  // expected-error@-1{{static_assert failed due to requirement 'constexpr_return_false<int>()'}}
+}
+template void test_template_parameter_from_default<ExampleTypes>();
+// expected-note@-1{{in instantiation of function template specialization 'test_template_parameter_from_default<ExampleTypes>' requested here}}
+
+template <class T, class U>
+void test_template_parameter_as_written() {
+  static_assert(X<typename T::T, U>());
+  // expected-error@-1{{static_assert failed due to requirement 'X<int, float>()'}}
+  static_assert(sizeof(X<typename T::T, U>) == 0);
+  // expected-error@-1{{static_assert failed due to requirement 'sizeof(X<int, float>) == 0'}}
   static_assert(constexpr_return_false<typename T::T, typename T::U>());
   // expected-error@-1{{static_assert failed due to requirement 'constexpr_return_false<int, float>()'}}
 }
-template void foo6<ExampleTypes>();
-// expected-note@-1{{in instantiation of function template specialization 'foo6<ExampleTypes>' requested here}}
+template void test_template_parameter_as_written<ExampleTypes, float>();
+// expected-note@-1{{in instantiation of function template specialization 'test_template_parameter_as_written<ExampleTypes, float>' requested here}}
+
+
+template<class T, class = decltype(sizeof(0))>
+struct is_pointerable { static constexpr bool value = false; };
+
+template<class T>
+struct is_pointerable<T, decltype(sizeof(T*))> { static constexpr bool value = true; };
+
+template<class T>
+void test_is_pointerable()
+{
+    static_assert(is_pointerable<T>::value);
+    // expected-error@-1{{due to requirement 'is_pointerable<int &>::value'}}
+    static_assert(not is_pointerable<T>::value);
+    // expected-error@-1{{due to requirement '!is_pointerable<int>::value'}}
+}
+template void test_is_pointerable<int&>();
+// expected-note@-1{{in instantiation of function template specialization 'test_is_pointerable<int &>' requested here}}
+template void test_is_pointerable<int>();
+// expected-note@-1{{in instantiation of function template specialization 'test_is_pointerable<int>' requested here}}
+
+
+// This test emulates std::vector with a default allocator.
+// FIXME: Ideally we would like to avoid printing the default here (print 'S1<V<int> *, void>::value')
+template<class> struct A {};
+template<class T, class = A<T>> struct V {};
+template<class C> void test_std_vector_like_from_default() {
+    static_assert(S1<C*, void>::value);
+    // expected-error@-1{{due to requirement 'S1<V<int, A<int> > *, void>::value'}}
+}
+template void test_std_vector_like_from_default<V<int>>();
+// expected-note@-1{{in instantiation of function template specialization 'test_std_vector_like_from_default<V<int, A<int> > >' requested here}}
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<DeclRefExpr>(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<VarTemplateSpecializationDecl>(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<Expr *, std::string>
 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: lib/AST/TypePrinter.cpp
===================================================================
--- lib/AST/TypePrinter.cpp
+++ lib/AST/TypePrinter.cpp
@@ -164,9 +164,33 @@
 
 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<typename T, typename U = int> {};`, and
+  //   `struct Some { using Class = float; };`
+  switch (QT.getTypePtr()->getTypeClass()) {
+    // `X<Some::Class>` gets canonicalized to `X<float, int>`. We disable
+    // canonicalization so that `printTag()`` can see the template parameters as
+    // written.
+    case Type::TemplateSpecialization:
+    // `X<Some::Class>*` gets canonicalized to `X<float, int>*`. Disabling
+    // canonicalization of the pointer/array will canonicalize the pointee when
+    // it is is processed, resulting in `X<float>*`.
+    case Type::Pointer:
+    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/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<ClassTemplateSpecializationDecl>(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: 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<int, MyClass>` instead of
-  /// `ns::SomeTemplate<Container::value_type, T>`.
-  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());
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to