AntonBikineev created this revision.
AntonBikineev added reviewers: rsmith, lebedev.ri.

This is an attempt to implement P0692 
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0692r1.html>
Please note a couple of things:

1. given that clang already suppresses access checks on explicit 
specializations of classes as an extension, I decided to not make the feature 
C++17-specific and backport it to previous standards. Not sure that this is 
right though;
2. I'm also not sure that ParseOptionalCXXScopeSpecifer in a declarator is the 
correct place for suppressing checks.


Repository:
  rC Clang

https://reviews.llvm.org/D43153

Files:
  include/clang/Parse/RAIIObjectsForParser.h
  include/clang/Sema/DeclSpec.h
  lib/Parse/ParseDecl.cpp
  lib/Parse/ParseDeclCXX.cpp
  lib/Parse/ParseTemplate.cpp
  test/CXX/drs/dr1xx.cpp
  test/SemaCXX/access.cpp

Index: test/SemaCXX/access.cpp
===================================================================
--- test/SemaCXX/access.cpp
+++ test/SemaCXX/access.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s
 
 class C {
     struct S; // expected-note {{previously declared 'private' here}}
@@ -169,3 +169,103 @@
   }
   void bar() { foo<void>(); }
 }
+
+namespace P0692 {
+  namespace class_ {
+    template <class>
+    class trait_type {};
+   
+    template <int>
+    class trait_nontype;
+
+    template <template <class> class>
+    class trait_template;
+
+    class A {
+      class B1 {}; // expected-note 2{{here}}
+
+      template <class>
+      class B2 {}; // expected-note 2{{here}}
+
+      class B3 {};
+   
+      static const int I = 0; // expected-note 4{{here}}
+    };
+   
+    template <class T>
+    class trait_type<A::B2<T>> {
+      void use_private(int = A::I) { // expected-error {{private}}
+        A::B2<int> error; // expected-error {{private}}
+      }
+    };
+
+    template <template <int> class T>
+    class trait_type<T<A::I>> {
+      static const int I = A::I; // expected-error {{private}}
+    };
+   
+    template <>
+    class trait_type<A::B1>
+      : A::B1 { // expected-error {{private}}
+      void use_private(int = A::I) { // expected-error {{private}}
+        A::B1 error; // expected-error {{private}}
+      }
+    };
+
+    template class trait_type<A::B3>;
+
+    template <>
+    class trait_nontype<A::I> {
+      static const int I = A::I; // expected-error {{private}}
+    };
+
+    template <>
+    class trait_template<A::B2> {
+      A::B2<int> error; // expected-error {{private}}
+    };
+  }
+
+  namespace function {
+    class A {
+      class B1 {}; // expected-note 1{{here}}
+
+      class B2 {};
+   
+      using Int = int; // expected-note 1{{here}}
+    };
+   
+    template <class T>
+    void foo1() {}
+
+    template <> void foo1<A::B1>() {
+        A::B1 error; // expected-error {{private}}
+    }
+
+    template void foo1<A::B2>();
+
+    template <class T>
+    void foo2(A::Int) {} // expected-error {{private}}
+  }
+
+  namespace variable {
+    class A {
+      class B1 {};
+
+      template <class>
+      class B2 {};
+   
+      static const int I = 0; // expected-note 2{{here}}
+    };
+
+    template <class T>
+    int var = 0;
+
+    template <>
+    int var<A::B1> =
+        A::I; // expected-error {{private}}
+
+    template <class T>
+    int var<A::B2<T>> =
+        A::I; // expected-error {{private}}
+  }
+}
Index: test/CXX/drs/dr1xx.cpp
===================================================================
--- test/CXX/drs/dr1xx.cpp
+++ test/CXX/drs/dr1xx.cpp
@@ -917,12 +917,12 @@
   template <class T> void C<T>::g() {}
 
   class A {
-    class B {}; // expected-note {{here}}
+    class B {};
     void f();
   };
 
   template void C<A::B>::f();
-  template <> void C<A::B>::g(); // expected-error {{private}}
+  template <> void C<A::B>::g();
 
   void A::f() {
     C<B> cb;
Index: lib/Parse/ParseTemplate.cpp
===================================================================
--- lib/Parse/ParseTemplate.cpp
+++ lib/Parse/ParseTemplate.cpp
@@ -235,7 +235,10 @@
 
   // Parse the declarator.
   ParsingDeclarator DeclaratorInfo(*this, DS, (DeclaratorContext)Context);
+  DeclaratorInfo.setTemplateDeclOrSpec(TemplateInfo.Kind !=
+                                       ParsedTemplateInfo::NonTemplate);
   ParseDeclarator(DeclaratorInfo);
+
   // Error parsing the declarator?
   if (!DeclaratorInfo.hasName()) {
     // If so, skip until the semi-colon or a }.
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -1375,15 +1375,22 @@
   //   The usual access checking rules do not apply to names used to specify
   //   explicit instantiations.
   //
-  // As an extension we do not perform access checking on the names used to
-  // specify explicit specializations either. This is important to allow
-  // specializing traits classes for private types.
+  // C++2a [temp.spec] 17.8/6:
+  //   The usual access checking rules do not apply to names in a declaration
+  //   of an explicit instantiation or explicit specialization, with the
+  //   exception of names appearing in a function body, default argument,
+  //   base-clause, member-specification, enumerator-list,
+  //   or static data member or variable template initializer
+  //
+  // C++2a [temp.class.spec] 17.6.5/10:
+  //   The usual access checking rules do not apply to non-dependent names
+  //   used to specify template arguments of the simple-template-id
+  //   of the partial specialization
   //
   // Note that we don't suppress if this turns out to be an elaborated
   // type specifier.
   bool shouldDelayDiagsInTag =
-    (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
-     TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
+    TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate;
   SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag);
 
   ParsedAttributesWithRange attrs(AttrFactory);
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -4073,12 +4073,23 @@
   // C++11 [temp.explicit]p12:
   //   The usual access controls do not apply to names used to specify
   //   explicit instantiations.
-  // We extend this to also cover explicit specializations.  Note that
-  // we don't suppress if this turns out to be an elaborated type
-  // specifier.
+  //
+  // C++2a [temp.spec] 17.8/6:
+  //   The usual access checking rules do not apply to names in a declaration
+  //   of an explicit instantiation or explicit specialization, with the
+  //   exception of names appearing in a function body, default argument,
+  //   base-clause, member-specification, enumerator-list,
+  //   or static data member or variable template initializer
+  //
+  // C++2a [temp.class.spec] 17.6.5/10:
+  //   The usual access checking rules do not apply to non-dependent names
+  //   used to specify template arguments of the simple-template-id
+  //   of the partial specialization
+  //
+  // Note that we don't suppress if this turns out to be an elaborated
+  // type specifier.
   bool shouldDelayDiagsInTag =
-    (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation ||
-     TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
+    TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate;
   SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag);
 
   // Enum definitions should not be parsed in a trailing-return-type.
@@ -5262,12 +5273,17 @@
        (Tok.is(tok::identifier) &&
         (NextToken().is(tok::coloncolon) || NextToken().is(tok::less))) ||
        Tok.is(tok::annot_cxxscope))) {
+    SuppressAccessChecks diagsFromDeclr(*this, D.isTemplateDeclOrSpec());
+
     bool EnteringContext =
         D.getContext() == DeclaratorContext::FileContext ||
         D.getContext() == DeclaratorContext::MemberContext;
     CXXScopeSpec SS;
     ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext);
 
+    if (D.isTemplateDeclOrSpec())
+      diagsFromDeclr.done();
+
     if (SS.isNotEmpty()) {
       if (Tok.isNot(tok::star)) {
         // The scope spec really belongs to the direct-declarator.
Index: include/clang/Sema/DeclSpec.h
===================================================================
--- include/clang/Sema/DeclSpec.h
+++ include/clang/Sema/DeclSpec.h
@@ -1797,6 +1797,10 @@
   /// Indicates whether the InlineParams / InlineBindings storage has been used.
   unsigned InlineStorageUsed : 1;
 
+  /// Indicates whether the declarator is part of a
+  /// function/variable template-declaration or an explicit-specialization
+  unsigned TemplateDeclOrSpec : 1;
+
   /// Attrs - Attributes.
   ParsedAttributes Attrs;
 
@@ -1832,7 +1836,8 @@
         GroupingParens(false), FunctionDefinition(FDK_Declaration),
         Redeclaration(false), Extension(false), ObjCIvar(false),
         ObjCWeakProperty(false), InlineStorageUsed(false),
-        Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr) {}
+        TemplateDeclOrSpec(false), Attrs(ds.getAttributePool().getFactory()),
+        AsmLabel(nullptr) {}
 
   ~Declarator() {
     clear();
@@ -1914,6 +1919,7 @@
     Attrs.clear();
     AsmLabel = nullptr;
     InlineStorageUsed = false;
+    TemplateDeclOrSpec = false;
     ObjCIvar = false;
     ObjCWeakProperty = false;
     CommaLoc = SourceLocation();
@@ -2415,6 +2421,9 @@
   void setObjCWeakProperty(bool Val = true) { ObjCWeakProperty = Val; }
   bool isObjCWeakProperty() const { return ObjCWeakProperty; }
 
+  void setTemplateDeclOrSpec(bool Val = true) { TemplateDeclOrSpec = Val; }
+  bool isTemplateDeclOrSpec() const { return TemplateDeclOrSpec; }
+
   void setInvalidType(bool Val = true) { InvalidType = Val; }
   bool isInvalidType() const {
     return InvalidType || DS.getTypeSpecType() == DeclSpec::TST_error;
Index: include/clang/Parse/RAIIObjectsForParser.h
===================================================================
--- include/clang/Parse/RAIIObjectsForParser.h
+++ include/clang/Parse/RAIIObjectsForParser.h
@@ -32,7 +32,8 @@
   /// dependent, in that they can only be resolved with full
   /// information about what's being declared.  They are also
   /// suppressed in certain contexts, like the template arguments of
-  /// an explicit instantiation.  However, those suppression contexts
+  /// an explicit instantiation and an explicit/partial
+  /// specialization. However, those suppression contexts
   /// cannot necessarily be fully determined in advance;  for
   /// example, something starting like this:
   ///   template <> class std::vector<A::PrivateType>
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D43153: [... Anton Bikineev via Phabricator via cfe-commits

Reply via email to