rsmith updated this revision to Diff 243022.
rsmith marked 4 inline comments as done.
rsmith added a comment.

Updates based on review comments from @rjmccall.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D74103

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaDecl.cpp
  clang/test/Analysis/inlining/eager-reclamation-path-notes.cpp
  clang/test/Analysis/padding_cpp.cpp
  clang/test/Analysis/padding_message.cpp
  clang/test/CXX/class/class.local/p4.cpp
  clang/test/CXX/class/class.union/p2-0x.cpp
  clang/test/CXX/drs/dr4xx.cpp
  clang/test/Modules/submodules-merge-defs.cpp
  clang/test/OpenMP/target_map_codegen.cpp
  clang/test/SemaCXX/anonymous-struct.cpp
  clang/test/SemaCXX/linkage.cpp
  clang/test/SemaCXX/linkage2.cpp
  clang/test/SemaCXX/undefined-internal.cpp
  clang/test/SemaCXX/warn-unused-filescoped.cpp
  clang/test/SemaCXX/warn-unused-local-typedef.cpp
  clang/test/SemaTemplate/instantiate-function-2.cpp
  clang/www/cxx_status.html

Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -1091,7 +1091,7 @@
     </tr>
       <tr> <!-- from Cologne -->
         <td><a href="https://wg21.link/p1766r1";>P1766R1</a> (<a href="#dr">DR</a>)</td>
-        <td rowspan="3" class="none" align="center">No</td>
+        <td rowspan="3" class="unreleased" align="center">Clang 11</td>
       </tr>
       <tr>
         <td><a href="https://wg21.link/p1811r0";>P1811R0</a></td>
Index: clang/test/SemaTemplate/instantiate-function-2.cpp
===================================================================
--- clang/test/SemaTemplate/instantiate-function-2.cpp
+++ clang/test/SemaTemplate/instantiate-function-2.cpp
@@ -49,11 +49,11 @@
 namespace AliasTagDef {
   template<typename T>
   T f() {
-    using S = struct {
+    using S = struct { // expected-warning {{add a tag name}} expected-note {{}}
 #if __cplusplus <= 199711L
     // expected-warning@-2 {{alias declarations are a C++11 extension}}
 #endif
-      T g() {
+      T g() { // expected-note {{}}
         return T();
       }
     };
Index: clang/test/SemaCXX/warn-unused-local-typedef.cpp
===================================================================
--- clang/test/SemaCXX/warn-unused-local-typedef.cpp
+++ clang/test/SemaCXX/warn-unused-local-typedef.cpp
@@ -108,13 +108,13 @@
 }
 
 void typedef_in_nested_name() {
-  typedef struct {
-    typedef int Foo;
-  } A;
+  typedef struct { // expected-warning {{add a tag name}}
+    typedef int Foo; // expected-note {{}}
+  } A; // expected-note {{}}
   A::Foo adsf;
 
-  using A2 = struct {
-    typedef int Foo;
+  using A2 = struct { // expected-warning {{add a tag name}} expected-note {{this alias declaration}}
+    typedef int Foo; // expected-note {{}}
   };
   A2::Foo adsf2;
 }
Index: clang/test/SemaCXX/warn-unused-filescoped.cpp
===================================================================
--- clang/test/SemaCXX/warn-unused-filescoped.cpp
+++ clang/test/SemaCXX/warn-unused-filescoped.cpp
@@ -164,9 +164,9 @@
 }
 
 namespace test6 {
-  typedef struct {
-    void bar();
-  } A;
+  typedef struct { // expected-warning {{add a tag name}}
+    void bar(); // expected-note {{}}
+  } A; // expected-note {{}}
 
   typedef struct {
     void bar();  // expected-warning {{unused member function 'bar'}}
Index: clang/test/SemaCXX/undefined-internal.cpp
===================================================================
--- clang/test/SemaCXX/undefined-internal.cpp
+++ clang/test/SemaCXX/undefined-internal.cpp
@@ -206,12 +206,12 @@
 }
 
 namespace test7 {
-  typedef struct {
-    void bar();
+  typedef struct { // expected-warning {{add a tag name}}
+    void bar(); // expected-note {{this member}}
     void foo() {
       bar();
     }
-  } A;
+  } A; // expected-note {{this typedef}}
 }
 
 namespace test8 {
Index: clang/test/SemaCXX/linkage2.cpp
===================================================================
--- clang/test/SemaCXX/linkage2.cpp
+++ clang/test/SemaCXX/linkage2.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=gnu++11 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++11-extensions -Wno-local-type-template-args %s -std=gnu++98
-// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++11-extensions -Wno-local-type-template-args -fmodules %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-non-c-typedef-for-linkage -std=gnu++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-non-c-typedef-for-linkage -Wno-c++11-extensions -Wno-local-type-template-args %s -std=gnu++98
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-non-c-typedef-for-linkage -Wno-c++11-extensions -Wno-local-type-template-args -fmodules %s
 
 namespace test1 {
   int x; // expected-note {{previous definition is here}}
@@ -245,7 +245,8 @@
     void f() { struct Inner {}; Use<Inner> ui; }
   } F;
 #if __cplusplus < 201103L
-  // expected-error@-2 {{unsupported: typedef changes linkage of anonymous type, but linkage was already computed}}
-  // expected-note@-5 {{use a tag name here}}
+  // expected-error@-4 {{given name for linkage purposes by typedef declaration after its linkage was computed}}
+  // expected-note@-4 {{due to this member}}
+  // expected-note@-4 {{by this typedef}}
 #endif
 }
Index: clang/test/SemaCXX/linkage.cpp
===================================================================
--- clang/test/SemaCXX/linkage.cpp
+++ clang/test/SemaCXX/linkage.cpp
@@ -3,7 +3,7 @@
 // compared against the earlier cached value.  If we had a way of
 // testing linkage directly in Sema, that would be better.
 
-// RUN: %clang_cc1 -Werror -triple x86_64-apple-darwin10 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -Werror -Wno-non-c-typedef-for-linkage -triple x86_64-apple-darwin10 -emit-llvm %s -o - | FileCheck %s
 
 // CHECK: @_ZZN5test61A3fooEvE3bar = linkonce_odr global i32 0, align 4
 
Index: clang/test/SemaCXX/anonymous-struct.cpp
===================================================================
--- clang/test/SemaCXX/anonymous-struct.cpp
+++ clang/test/SemaCXX/anonymous-struct.cpp
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s
 
 struct S {
   S();
@@ -27,15 +28,69 @@
 };
 
 template <class T> void foo(T);
-typedef struct { // expected-note {{use a tag name here to establish linkage prior to definition}}
+typedef struct { // expected-error {{anonymous non-C-compatible type given name for linkage purposes by typedef declaration after its linkage was computed; add a tag name here to establish linkage prior to definition}}
 #if __cplusplus <= 199711L
 // expected-note@-2 {{declared here}}
 #endif
 
-  void test() {
+  void test() { // expected-note {{type is not C-compatible due to this member declaration}}
     foo(this);
 #if __cplusplus <= 199711L
     // expected-warning@-2 {{template argument uses unnamed type}}
 #endif
   }
-} A; // expected-error {{unsupported: typedef changes linkage of anonymous type, but linkage was already computed}}
+} A; // expected-note {{type is given name 'A' for linkage purposes by this typedef declaration}}
+
+typedef struct { // expected-warning {{anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here}}
+  int x = 0; // expected-note {{type is not C-compatible due to this default member initializer}} expected-warning 0-1{{extension}}
+} B; // expected-note {{type is given name 'B' for linkage purposes by this typedef declaration}}
+
+typedef struct // expected-warning {{anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here}}
+: B { // expected-note {{type is not C-compatible due to this base class}}
+} C; // expected-note {{type is given name 'C' for linkage purposes by this typedef declaration}}
+
+#if __cplusplus > 201703L
+typedef struct { // expected-warning {{anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here}}
+  static_assert([]{ return true; }()); // expected-note {{type is not C-compatible due to this lambda expression}}
+} Lambda1; // expected-note {{type is given name 'Lambda1' for linkage purposes by this typedef declaration}}
+
+template<int> struct X {};
+typedef struct { // expected-warning {{anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here}}
+  X<[]{ return 0; }()> x; // expected-note {{type is not C-compatible due to this lambda expression}}
+  // FIXME: expected-error@-1 {{lambda expression cannot appear}}
+} Lambda2; // expected-note {{type is given name 'Lambda2' for linkage purposes by this typedef declaration}}
+
+typedef struct { // expected-warning {{anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here}}
+  enum E {
+    a = []{ return 1; }() // expected-note {{type is not C-compatible due to this lambda expression}}
+  };
+} Lambda3; // expected-note {{type is given name 'Lambda3' for linkage purposes by this typedef declaration}}
+#endif
+
+typedef struct { // expected-warning {{anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here}}
+  template<int> void f() {} // expected-note {{type is not C-compatible due to this member declaration}}
+} Template; // expected-note {{type is given name 'Template' for linkage purposes by this typedef declaration}}
+
+typedef struct { // expected-warning {{anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here}}
+  struct U {
+    void f(); // expected-note {{type is not C-compatible due to this member declaration}}
+  };
+} Nested; // expected-note {{type is given name 'Nested' for linkage purposes by this typedef declaration}}
+
+// Check that we don't diagnose the permitted cases:
+typedef struct {
+  // (non-members)
+  _Static_assert(true, "");
+  int : 0;
+  /*empty-declaration*/;
+
+  // non-static data members
+  int a;
+  // member enumerations
+  enum E { x, y, z };
+  // member classes
+  struct S {};
+
+  // recursively
+  struct T { int a; };
+} OK;
Index: clang/test/OpenMP/target_map_codegen.cpp
===================================================================
--- clang/test/OpenMP/target_map_codegen.cpp
+++ clang/test/OpenMP/target_map_codegen.cpp
@@ -5165,7 +5165,7 @@
   int *ptrBase1;
 } Base;
 
-typedef struct : public Base {
+typedef struct StructWithPtrTag : public Base {
   int *ptr;
   int *ptr2;
   int val;
Index: clang/test/Modules/submodules-merge-defs.cpp
===================================================================
--- clang/test/Modules/submodules-merge-defs.cpp
+++ clang/test/Modules/submodules-merge-defs.cpp
@@ -10,6 +10,8 @@
 #include "empty.h"
 #ifdef EARLY_INDIRECT_INCLUDE
 #include "indirect.h"
+// expected-warning@defs.h:28 3{{anonymous non-C-compatible type}}
+// expected-note@defs.h:28 6{{type is}}
 #endif
 
 A pre_a;
Index: clang/test/CXX/drs/dr4xx.cpp
===================================================================
--- clang/test/CXX/drs/dr4xx.cpp
+++ clang/test/CXX/drs/dr4xx.cpp
@@ -82,6 +82,9 @@
   typedef struct {
     static int n; // expected-error {{static data member 'n' not allowed in anonymous struct}}
   } A;
+  typedef union {
+    static int n; // expected-error {{static data member 'n' not allowed in anonymous union}}
+  } B;
 }
 
 namespace dr407 { // dr407: 3.8
Index: clang/test/CXX/class/class.union/p2-0x.cpp
===================================================================
--- clang/test/CXX/class/class.union/p2-0x.cpp
+++ clang/test/CXX/class/class.union/p2-0x.cpp
@@ -37,12 +37,12 @@
 
 struct S {
   union {
-    static const int n; // expected-error {{static members cannot be declared in an anonymous union}}
+    static const int n; // expected-error {{static data member 'n' not allowed in anonymous union}}
     int a;
     int b;
   };
 };
 static union {
-  static const int k; // expected-error {{static members cannot be declared in an anonymous union}}
+  static const int k; // expected-error {{static data member 'k' not allowed in anonymous union}}
   int n;
 };
Index: clang/test/CXX/class/class.local/p4.cpp
===================================================================
--- clang/test/CXX/class/class.local/p4.cpp
+++ clang/test/CXX/class/class.local/p4.cpp
@@ -2,9 +2,9 @@
 
 void f() {
   struct X {
-    static int a; // expected-error {{static data member 'a' not allowed in local class 'X'}}
+    static int a; // expected-error {{static data member 'a' not allowed in local struct 'X'}}
     int b;
-    
+
     static void f() { }
   };
 }
Index: clang/test/Analysis/padding_message.cpp
===================================================================
--- clang/test/Analysis/padding_message.cpp
+++ clang/test/Analysis/padding_message.cpp
@@ -265,13 +265,13 @@
 };
 
 // expected-warning@+7{{\
-Excessive padding in 'TypedefSandwich2' (6 padding bytes, where 2 is optimal). \
+Excessive padding in 'struct TypedefSandwich2' (6 padding bytes, where 2 is optimal). \
 Optimal fields order: \
 t, \
 c1, \
 c2, \
 }}
-typedef struct {
+typedef struct TypedefSandwich2 {
   char c1;
   // expected-warning@+7{{\
 Excessive padding in 'TypedefSandwich2::NestedTypedef' (6 padding bytes, where 2 is optimal). \
Index: clang/test/Analysis/padding_cpp.cpp
===================================================================
--- clang/test/Analysis/padding_cpp.cpp
+++ clang/test/Analysis/padding_cpp.cpp
@@ -165,7 +165,7 @@
   TemplateSandwich<void *> t3;
 };
 
-typedef struct { // expected-warning{{Excessive padding in 'TypedefSandwich2'}}
+typedef struct TypedefSandwich2 { // expected-warning{{Excessive padding in 'struct TypedefSandwich2'}}
   char c1;
   typedef struct { // expected-warning{{Excessive padding in 'TypedefSandwich2::NestedTypedef'}}
     char c1;
Index: clang/test/Analysis/inlining/eager-reclamation-path-notes.cpp
===================================================================
--- clang/test/Analysis/inlining/eager-reclamation-path-notes.cpp
+++ clang/test/Analysis/inlining/eager-reclamation-path-notes.cpp
@@ -2,9 +2,9 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-output=plist-multi-file -analyzer-config graph-trim-interval=5 -analyzer-config suppress-null-return-paths=false %s -o %t.plist
 // RUN: %normalize_plist <%t.plist | diff -ub %S/Inputs/expected-plists/eager-reclamation-path-notes.cpp.plist -
 
-typedef struct {
+struct IntWrapper {
   int getValue();
-} IntWrapper;
+};
 
 IntWrapper *getNullWrapper() {
   return 0;
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -4346,6 +4346,81 @@
   }
 }
 
+namespace {
+enum class NonCLikeKind {
+  None,
+  BaseClass,
+  DefaultMemberInit,
+  Lambda,
+  Friend,
+  OtherMember,
+  Invalid,
+};
+}
+
+/// Determine whether a class is C-like, according to the rules of C++
+/// [dcl.typedef] for anonymous classes with typedef names for linkage.
+static std::pair<NonCLikeKind, SourceRange>
+getNonCLikeKindForAnonymousStruct(const CXXRecordDecl *RD) {
+  if (RD->isInvalidDecl())
+    return {NonCLikeKind::Invalid, {}};
+
+  // C++ [dcl.typedef]p9: [P1766R1]
+  //   An unnamed class with a typedef name for linkage purposes shall not
+  //
+  //    -- have any base classes
+  if (RD->getNumBases())
+    return {NonCLikeKind::BaseClass,
+            SourceRange(RD->bases_begin()->getBeginLoc(),
+                        RD->bases_end()[-1].getEndLoc())};
+  bool Invalid = false;
+  for (Decl *D : RD->decls()) {
+    // Don't complain about things we already diagnosed.
+    if (D->isInvalidDecl()) {
+      Invalid = true;
+      continue;
+    }
+
+    //  -- have any [...] default member initializers
+    if (auto *FD = dyn_cast<FieldDecl>(D)) {
+      if (FD->hasInClassInitializer()) {
+        auto *Init = FD->getInClassInitializer();
+        return {NonCLikeKind::DefaultMemberInit,
+                Init ? Init->getSourceRange() : D->getSourceRange()};
+      }
+      continue;
+    }
+
+    // FIXME: We don't allow friend declarations. This violates the wording of
+    // P1766, but not the intent.
+    if (isa<FriendDecl>(D))
+      return {NonCLikeKind::Friend, D->getSourceRange()};
+
+    //  -- declare any members other than non-static data members, member
+    //     enumerations, or member classes,
+    if (isa<StaticAssertDecl>(D) || isa<IndirectFieldDecl>(D) ||
+        isa<EnumDecl>(D))
+      continue;
+    auto *MemberRD = dyn_cast<CXXRecordDecl>(D);
+    if (!MemberRD)
+      return {NonCLikeKind::OtherMember, D->getSourceRange()};
+
+    //  -- contain a lambda-expression,
+    if (MemberRD->isLambda())
+      return {NonCLikeKind::Lambda, MemberRD->getSourceRange()};
+
+    //  and all member classes shall also satisfy these requirements
+    //  (recursively).
+    if (MemberRD->isThisDeclarationADefinition()) {
+      auto Kind = getNonCLikeKindForAnonymousStruct(MemberRD);
+      if (Kind.first != NonCLikeKind::None)
+        return Kind;
+    }
+  }
+
+  return {Invalid ? NonCLikeKind::Invalid : NonCLikeKind::None, {}};
+}
+
 void Sema::setTagNameForLinkagePurposes(TagDecl *TagFromDeclSpec,
                                         TypedefNameDecl *NewTD) {
   if (TagFromDeclSpec->isInvalidDecl())
@@ -4366,27 +4441,40 @@
     return;
   }
 
-  // If we've already computed linkage for the anonymous tag, then
-  // adding a typedef name for the anonymous decl can change that
-  // linkage, which might be a serious problem.  Diagnose this as
-  // unsupported and ignore the typedef name.  TODO: we should
-  // pursue this as a language defect and establish a formal rule
-  // for how to handle it.
-  if (TagFromDeclSpec->hasLinkageBeenComputed()) {
-    Diag(NewTD->getLocation(), diag::err_typedef_changes_linkage);
+  // C++ [dcl.typedef]p9: [P1766R1, applied as DR]
+  //   An unnamed class with a typedef name for linkage purposes shall [be
+  //   C-like].
+  if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(TagFromDeclSpec)) {
+    auto Kind = getNonCLikeKindForAnonymousStruct(RD);
+    if (Kind.first == NonCLikeKind::Invalid)
+      return;
+    if (Kind.first != NonCLikeKind::None) {
+      bool Extension = !TagFromDeclSpec->hasLinkageBeenComputed();
 
-    SourceLocation tagLoc = TagFromDeclSpec->getInnerLocStart();
-    tagLoc = getLocForEndOfToken(tagLoc);
+      SourceLocation FixitLoc =
+          getLocForEndOfToken(TagFromDeclSpec->getInnerLocStart());
+      llvm::SmallString<40> TextToInsert;
+      TextToInsert += ' ';
+      TextToInsert += NewTD->getIdentifier()->getName();
 
-    llvm::SmallString<40> textToInsert;
-    textToInsert += ' ';
-    textToInsert += NewTD->getIdentifier()->getName();
-    Diag(tagLoc, diag::note_typedef_changes_linkage)
-        << FixItHint::CreateInsertion(tagLoc, textToInsert);
-    return;
+      Diag(FixitLoc, Extension ? diag::ext_non_c_like_anon_struct_in_typedef
+                               : diag::err_non_c_like_anon_struct_in_typedef)
+          << isa<TypeAliasDecl>(NewTD)
+          << FixItHint::CreateInsertion(FixitLoc, TextToInsert);
+      Diag(Kind.second.getBegin(), diag::note_non_c_like_anon_struct)
+        << int(Kind.first) - 1 << Kind.second;
+      Diag(NewTD->getLocation(), diag::note_typedef_for_linkage_here)
+        << NewTD << isa<TypeAliasDecl>(NewTD);
+      if (!Extension)
+        return;
+    }
   }
 
-  // Otherwise, set this is the anon-decl typedef for the tag.
+  // If we compute the linkage of a C-compatible type early, that's a bug.
+  assert(!TagFromDeclSpec->hasLinkageBeenComputed() &&
+         "linkage unexpectedly computed for C-compatible anonymous type");
+
+  // Otherwise, set this as the anon-decl typedef for the tag.
   TagFromDeclSpec->setTypedefNameForAnonDecl(NewTD);
 }
 
@@ -4917,6 +5005,10 @@
     //   define non-static data members. [Note: nested types and
     //   functions cannot be declared within an anonymous union. ]
     for (auto *Mem : Record->decls()) {
+      // Ignore invalid declarations; we already diagnosed them.
+      if (Mem->isInvalidDecl())
+        continue;
+
       if (auto *FD = dyn_cast<FieldDecl>(Mem)) {
         // C++ [class.union]p3:
         //   An anonymous union shall not have private or protected
@@ -6757,28 +6849,33 @@
 
     if (SC == SC_Static && CurContext->isRecord()) {
       if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) {
-        if (RD->isLocalClass())
+        // C++ [class.static.data]p2:
+        //   A static data member shall not be a direct member of an unnamed
+        //   or local class
+        // FIXME: or of a (possibly indirectly) nested class thereof.
+        if (RD->isLocalClass()) {
           Diag(D.getIdentifierLoc(),
                diag::err_static_data_member_not_allowed_in_local_class)
-            << Name << RD->getDeclName();
-
-        // C++98 [class.union]p1: If a union contains a static data member,
-        // the program is ill-formed. C++11 drops this restriction.
-        if (RD->isUnion())
+            << Name << RD->getDeclName() << RD->getTagKind();
+        } else if (!RD->getDeclName()) {
+          Diag(D.getIdentifierLoc(),
+               diag::err_static_data_member_not_allowed_in_anon_struct)
+            << Name << RD->getTagKind();
+          Invalid = true;
+        } else if (RD->isUnion()) {
+          // C++98 [class.union]p1: If a union contains a static data member,
+          // the program is ill-formed. C++11 drops this restriction.
           Diag(D.getIdentifierLoc(),
                getLangOpts().CPlusPlus11
                  ? diag::warn_cxx98_compat_static_data_member_in_union
                  : diag::ext_static_data_member_in_union) << Name;
-        // We conservatively disallow static data members in anonymous structs.
-        else if (!RD->getDeclName())
-          Diag(D.getIdentifierLoc(),
-               diag::err_static_data_member_not_allowed_in_anon_struct)
-            << Name << RD->isUnion();
+        }
       }
     }
 
     // Match up the template parameter lists with the scope specifier, then
     // determine whether we have a template or a template specialization.
+    bool InvalidScope = false;
     TemplateParams = MatchTemplateParametersToScopeSpecifier(
         D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(),
         D.getCXXScopeSpec(),
@@ -6786,7 +6883,8 @@
             ? D.getName().TemplateId
             : nullptr,
         TemplateParamLists,
-        /*never a friend*/ false, IsMemberSpecialization, Invalid);
+        /*never a friend*/ false, IsMemberSpecialization, InvalidScope);
+    Invalid |= InvalidScope;
 
     if (TemplateParams) {
       if (!TemplateParams->size() &&
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -788,10 +788,23 @@
 def ext_typedef_without_a_name : ExtWarn<"typedef requires a name">,
   InGroup<MissingDeclarations>;
 def err_typedef_not_identifier : Error<"typedef name must be an identifier">;
-def err_typedef_changes_linkage : Error<"unsupported: typedef changes linkage"
-  " of anonymous type, but linkage was already computed">;
-def note_typedef_changes_linkage : Note<"use a tag name here to establish "
-  "linkage prior to definition">;
+
+def ext_non_c_like_anon_struct_in_typedef : ExtWarn<
+  "anonymous non-C-compatible type given name for linkage purposes "
+  "by %select{typedef|alias}0 declaration; "
+  "add a tag name here">, InGroup<DiagGroup<"non-c-typedef-for-linkage">>;
+def err_non_c_like_anon_struct_in_typedef : Error<
+  "anonymous non-C-compatible type given name for linkage purposes "
+  "by %select{typedef|alias}0 declaration after its linkage was computed; "
+  "add a tag name here to establish linkage prior to definition">;
+def note_non_c_like_anon_struct : Note<
+  "type is not C-compatible due to this "
+  "%select{base class|default member initializer|lambda expression|"
+  "friend declaration|member declaration}0">;
+def note_typedef_for_linkage_here : Note<
+  "type is given name %0 for linkage purposes by this "
+  "%select{typedef|alias}1 declaration">;
+
 def err_statically_allocated_object : Error<
   "interface type cannot be statically allocated">;
 def err_object_cannot_be_passed_returned_by_value : Error<
@@ -1796,8 +1809,13 @@
   "because type %0 has a member with %select{no|no|__strong|__weak|"
   "__autoreleasing}1 ownership">;
 
+/// Selector for a TagTypeKind value.
+def select_tag_type_kind : TextSubstitution<
+  "%select{struct|interface|union|class|enum}0">;
+
 def err_static_data_member_not_allowed_in_anon_struct : Error<
-  "static data member %0 not allowed in anonymous struct">;
+  "static data member %0 not allowed in anonymous "
+  "%sub{select_tag_type_kind}1">;
 def ext_static_data_member_in_union : ExtWarn<
   "static data member %0 in union is a C++11 extension">, InGroup<CXX11>;
 def warn_cxx98_compat_static_data_member_in_union : Warning<
@@ -8114,7 +8132,7 @@
   "%select{%3|block literal|lambda expression|context}2">;
 
 def err_static_data_member_not_allowed_in_local_class : Error<
-  "static data member %0 not allowed in local class %1">;
+  "static data member %0 not allowed in local %sub{select_tag_type_kind}2 %1">;
 
 // C++ derived classes
 def err_base_clause_on_union : Error<"unions cannot have base classes">;
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -99,6 +99,43 @@
 C++ Language Changes in Clang
 -----------------------------
 
+- Clang now implements a restriction on giving non-C-compatible anonymous
+  structs a typedef name for linkage purposes, as described in C++ committee
+  paper `P1766R1 <http://wg21.link/p1766r1>`. This paper was adopted by the
+  C++ committee as a Defect Report resolution, so it is applied retroactively
+  to all C++ standard versions. This affects code such as:
+
+  .. code-block:: c++
+
+    typedef struct {
+      int f() { return 0; }
+    } S;
+
+  Previous versions of Clang rejected some constructs of this form
+  (specifically, where the linkage of the type happened to be computed
+  before the parser reached the typedef name); those cases are still rejected
+  in Clang 11.  In addition, cases that previous versions of Clang did not
+  reject now produce an extension warning. This warning can be disabled with
+  the warning flag ``-Wno-non-c-typedef-for-linkage``.
+
+  Affected code should be updated to provide a tag name for the anonymous
+  struct:
+
+  .. code-block:: c++
+
+    struct S {
+      int f() { return 0; }
+    };
+
+  If the code is shared with a C compilation (for example, if the parts that
+  are not C-compatible are guarded with ``#ifdef __cplusplus``), the typedef
+  declaration should be retained, but a tag name should still be provided:
+
+  .. code-block:: c++
+
+    typedef struct S {
+      int f() { return 0; }
+    } S;
 
 C++1z Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to