]> granicus.if.org Git - clang/commitdiff
[Sema] Fix a crash-on-invalid when a template parameter list has a class
authorAkira Hatanaka <ahatanaka@apple.com>
Mon, 26 Jun 2017 18:46:12 +0000 (18:46 +0000)
committerAkira Hatanaka <ahatanaka@apple.com>
Mon, 26 Jun 2017 18:46:12 +0000 (18:46 +0000)
definition or non-reference class type.

The crash occurs when there is a template parameter list in a class that
is missing the closing angle bracket followed by a definition of a
struct. For example:

class C0 {
public:
  template<typename T, typename T1 = T // missing closing angle bracket
  struct S0 {};

  C0() : m(new S0<int>) {}
  S0<int> *m;
};

This happens because the parsed struct is added to the scope of the
enclosing class without having its access specifier set, which results
in an assertion failure in SemaAccess.cpp later.

This commit fixes the crash by adding the parsed struct to the enclosing
file scope and marking structs as invalid if they are defined in
template parameter lists.

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

Differential Revision: https://reviews.llvm.org/D33606

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@306317 91177308-0d34-0410-b5e6-96231b3b80d8

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 [new file with mode: 0644]

index 2373f1048fe0470638e01ac1232d3a168feda92b..21d699ec402e54242154a5e5cbc3555b9d721cf2 100644 (file)
@@ -1852,6 +1852,7 @@ private:
     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
@@ -1862,6 +1863,7 @@ private:
   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:
@@ -1882,6 +1884,7 @@ private:
   static bool isClassTemplateDeductionContext(DeclSpecContext DSC) {
     switch (DSC) {
     case DSC_normal:
+    case DSC_template_param:
     case DSC_class:
     case DSC_top_level:
     case DSC_condition:
index 1dedf4ba24cea392126bee536c37609ef27edb6f..02c133d1c4fc509c9495c541424264a187318e79 100644 (file)
@@ -2153,7 +2153,8 @@ public:
                  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 22696a957a10ef408ef4a54156f120bc2e8ce0bc..d0ce9fc8958335f17b36cf566668b3eceb69ece1 100644 (file)
@@ -2629,6 +2629,8 @@ Parser::getDeclSpecContextFromDeclaratorContext(unsigned Context) {
     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)
@@ -4261,7 +4263,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
                                    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 1a4607a84cff26471f29eba6bb2c71f8f946af13..a724fa242268b80f794d3854d69b45a39fc28ef8 100644 (file)
@@ -1887,7 +1887,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
                                        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 37c80fe5e5201ac1ae6aaeb8f3a2917554ebc175..944cd775d52a4eef8eff1b153584c24e8e5762a9 100644 (file)
@@ -674,7 +674,8 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) {
   // 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 b94b8d9e4c1cd20d28c2a814172d473376ad72d3..e340456bc6dad7611edadc7b5fd5622fa0699e3d 100644 (file)
@@ -13090,7 +13090,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
                      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) &&
@@ -13360,11 +13361,11 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
   // 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
       //
@@ -13797,7 +13798,8 @@ CreateNewDecl:
 
   // 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 0b46e15bb0a3286a872c4b20b0fc70f9a7ff63db..453ece9d9c4d17cacd194dc9a0a498a1ed0268a0 100644 (file)
@@ -13394,7 +13394,8 @@ Decl *Sema::ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc,
                       /*ScopedEnumKWLoc=*/SourceLocation(),
                       /*ScopedEnumUsesClassTag=*/false,
                       /*UnderlyingType=*/TypeResult(),
-                      /*IsTypeSpecifier=*/false);
+                      /*IsTypeSpecifier=*/false,
+                      /*IsTemplateParamOrArg=*/false);
     }
 
     NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
index 1eea151a4ec8b7b44374d2f82bbcc5824bd3c145..a8923ce9e27df2cfb2ce58a2bc9bb380c4b14300 100644 (file)
@@ -8612,7 +8612,8 @@ Sema::ActOnExplicitInstantiation(Scope *S,
                         /*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 7140ac79f08936c6a03417457dba7848875a62c2..efa4faaacd6932ba1fd1fcc59f12ed1a056e4b47 100644 (file)
@@ -10,7 +10,6 @@ class Base { };
 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}}
 
diff --git a/test/SemaCXX/invalid-template-params.cpp b/test/SemaCXX/invalid-template-params.cpp
new file mode 100644 (file)
index 0000000..0c463fe
--- /dev/null
@@ -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}}
+};