ahatanak created this revision.

Currently clang crashes with an assertion failure in SemaAccess.cpp ("Assertion 
failed: (Access == AS_private || Access == AS_protected)") when compiling the 
following invalid code:

  class C0 {
  public:
    template<typename T0, typename T1 = T0 // missing closing angle bracket
    struct S0 {};
  
    C0() : m(new S0<int>) {}
    S0<int> *m;
  
    template<class C1 *p>
    void foo1();
  };
  
  C1 *x;

This seems to happen because:

- The definition of struct S0 is parsed as part of the template parameters list 
because of the missing closing angle bracket.
- Sema adds struct S0 to class C0's scope. But if struct S0 is introduced in a 
template parameter list, it should add the struct to the enclosing scope (the 
file scope), just like it would add class C1 to the file scope.
- It looks up S0 in C0's scope and then checks its access specifier when 
LookupResult is destructed. The assertion fails because S0's access specifier 
is not set (it's AS_none).

To fix the crash, I made changes in Sema::ActOnTag to check whether it's 
parsing a template parameter list and, if it is, move S0 to the correct scope. 
Also, the TagDecl is marked as invalid if the class is defined in a template 
parameter list. Note that test/SemaCXX/PR16677.cpp loses one error diagnostic 
because of this change.

rdar://problem/31783961
rdar://problem/19570630


https://reviews.llvm.org/D33606

Files:
  include/clang/Parse/Parser.h
  include/clang/Sema/Sema.h
  lib/Parse/ParseDecl.cpp
  lib/Parse/ParseDeclCXX.cpp
  lib/Parse/ParseTemplate.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclCXX.cpp
  lib/Sema/SemaTemplate.cpp
  test/SemaCXX/PR16677.cpp
  test/SemaCXX/invalid-template-params.cpp

Index: test/SemaCXX/invalid-template-params.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/invalid-template-params.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
+
+template<class> class Foo {
+  template<class UBar // expected-error {{expected ';' after class}}
+                      // expected-note@-1 {{'UBar' declared here}}
+  void foo1(); // expected-error {{a non-type template parameter cannot have type 'class UBar'}}
+               // expected-error@-1 {{expected ',' or '>' in template-parameter-list}}
+               // expected-warning@-2 {{declaration does not declare anything}}
+};
+
+Foo<int>::UBar g1; // expected-error {{no type named 'UBar' in 'Foo<int>'}}
+
+class C0 {
+public:
+  template<typename T0, typename T1 = T0 // missing closing angle bracket
+  struct S0 {}; // expected-error {{'S0' cannot be defined in a type specifier}}
+                // expected-error@-1 {{cannot combine with previous 'type-name' declaration specifier}}
+                // expected-error@-2 {{expected ',' or '>' in template-parameter-list}}
+                // expected-warning@-3 {{declaration does not declare anything}}
+  C0() : m(new S0<int>) {} // expected-error {{expected '(' for function-style cast or type construction}}
+                           // expected-error@-1 {{expected expression}}
+  S0<int> *m; // expected-error {{expected member name or ';' after declaration specifiers}}
+};
Index: test/SemaCXX/PR16677.cpp
===================================================================
--- test/SemaCXX/PR16677.cpp
+++ test/SemaCXX/PR16677.cpp
@@ -10,7 +10,6 @@
 template<class T,  // Should be angle bracket instead of comma
 class Derived : public Base<T> { // expected-error{{'Derived' cannot be defined in a type specifier}}
   Class_With_Destructor member;
-}; // expected-error{{a non-type template parameter cannot have type 'class Derived'}}
-   // expected-error@-1{{expected ',' or '>' in template-parameter-list}}
-   // expected-warning@-2{{declaration does not declare anything}}
+}; // expected-error{{expected ',' or '>' in template-parameter-list}}
+   // expected-warning@-1{{declaration does not declare anything}}
 
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -8609,7 +8609,8 @@
                         /*ModulePrivateLoc=*/SourceLocation(),
                         MultiTemplateParamsArg(), Owned, IsDependent,
                         SourceLocation(), false, TypeResult(),
-                        /*IsTypeSpecifier*/false);
+                        /*IsTypeSpecifier*/false,
+                        /*IsTemplateParamOrArg*/false);
   assert(!IsDependent && "explicit instantiation of dependent name not yet handled");
 
   if (!TagD)
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -13409,7 +13409,8 @@
                       /*ScopedEnumKWLoc=*/SourceLocation(),
                       /*ScopedEnumUsesClassTag=*/false,
                       /*UnderlyingType=*/TypeResult(),
-                      /*IsTypeSpecifier=*/false);
+                      /*IsTypeSpecifier=*/false,
+                      /*IsTemplateParamOrArg=*/false);
     }
 
     NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -13035,7 +13035,8 @@
                      SourceLocation ScopedEnumKWLoc,
                      bool ScopedEnumUsesClassTag,
                      TypeResult UnderlyingType,
-                     bool IsTypeSpecifier, SkipBodyInfo *SkipBody) {
+                     bool IsTypeSpecifier, bool IsTemplateParamOrArg,
+                     SkipBodyInfo *SkipBody) {
   // If this is not a definition, it must have a name.
   IdentifierInfo *OrigName = Name;
   assert((Name != nullptr || TUK == TUK_Definition) &&
@@ -13305,11 +13306,11 @@
   // also need to do a redeclaration lookup there, just in case
   // there's a shadow friend decl.
   if (Name && Previous.empty() &&
-      (TUK == TUK_Reference || TUK == TUK_Friend)) {
+      (TUK == TUK_Reference || TUK == TUK_Friend || IsTemplateParamOrArg)) {
     if (Invalid) goto CreateNewDecl;
     assert(SS.isEmpty());
 
-    if (TUK == TUK_Reference) {
+    if (TUK == TUK_Reference || IsTemplateParamOrArg) {
       // C++ [basic.scope.pdecl]p5:
       //   -- for an elaborated-type-specifier of the form
       //
@@ -13742,7 +13743,8 @@
 
   // C++11 [dcl.type]p3:
   //   A type-specifier-seq shall not define a class or enumeration [...].
-  if (getLangOpts().CPlusPlus && IsTypeSpecifier && TUK == TUK_Definition) {
+  if (getLangOpts().CPlusPlus && (IsTypeSpecifier || IsTemplateParamOrArg) &&
+      TUK == TUK_Definition) {
     Diag(New->getLocation(), diag::err_type_defined_in_type_specifier)
       << Context.getTagDeclType(New);
     Invalid = true;
Index: lib/Parse/ParseTemplate.cpp
===================================================================
--- lib/Parse/ParseTemplate.cpp
+++ lib/Parse/ParseTemplate.cpp
@@ -674,7 +674,8 @@
   // FIXME: The type should probably be restricted in some way... Not all
   // declarators (parts of declarators?) are accepted for parameters.
   DeclSpec DS(AttrFactory);
-  ParseDeclarationSpecifiers(DS);
+  ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none,
+                             DSC_template_param);
 
   // Parse this as a typename.
   Declarator ParamDecl(DS, Declarator::TemplateParamContext);
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -1885,7 +1885,8 @@
                                        SourceLocation(), false,
                                        clang::TypeResult(),
                                        DSC == DSC_type_specifier,
-                                       &SkipBody);
+                                       DSC == DSC_template_param ||
+                                       DSC == DSC_template_type_arg, &SkipBody);
 
     // If ActOnTag said the type was dependent, try again with the
     // less common call.
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -2628,6 +2628,8 @@
     return DSC_class;
   if (Context == Declarator::FileContext)
     return DSC_top_level;
+  if (Context == Declarator::TemplateParamContext)
+    return DSC_template_param;
   if (Context == Declarator::TemplateTypeArgContext)
     return DSC_template_type_arg;
   if (Context == Declarator::TrailingReturnContext)
@@ -4259,7 +4261,9 @@
                                    AS, DS.getModulePrivateSpecLoc(), TParams,
                                    Owned, IsDependent, ScopedEnumKWLoc,
                                    IsScopedUsingClassTag, BaseType,
-                                   DSC == DSC_type_specifier, &SkipBody);
+                                   DSC == DSC_type_specifier,
+                                   DSC == DSC_template_param ||
+                                   DSC == DSC_template_type_arg, &SkipBody);
 
   if (SkipBody.ShouldSkip) {
     assert(TUK == Sema::TUK_Definition && "can only skip a definition");
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -2104,7 +2104,8 @@
                  bool &OwnedDecl, bool &IsDependent,
                  SourceLocation ScopedEnumKWLoc,
                  bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
-                 bool IsTypeSpecifier, SkipBodyInfo *SkipBody = nullptr);
+                 bool IsTypeSpecifier, bool IsTemplateParamOrArg,
+                 SkipBodyInfo *SkipBody = nullptr);
 
   Decl *ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc,
                                 unsigned TagSpec, SourceLocation TagLoc,
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -1849,6 +1849,7 @@
     DSC_trailing, // C++11 trailing-type-specifier in a trailing return type
     DSC_alias_declaration, // C++11 type-specifier-seq in an alias-declaration
     DSC_top_level, // top-level/namespace declaration context
+    DSC_template_param, // template parameter context
     DSC_template_type_arg, // template type argument context
     DSC_objc_method_result, // ObjC method result context, enables 'instancetype'
     DSC_condition // condition declaration context
@@ -1859,6 +1860,7 @@
   static bool isTypeSpecifier(DeclSpecContext DSC) {
     switch (DSC) {
     case DSC_normal:
+    case DSC_template_param:
     case DSC_class:
     case DSC_top_level:
     case DSC_objc_method_result:
@@ -1879,6 +1881,7 @@
   static bool isClassTemplateDeductionContext(DeclSpecContext DSC) {
     switch (DSC) {
     case DSC_normal:
+    case DSC_template_param:
     case DSC_class:
     case DSC_top_level:
     case DSC_condition:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to