[clang] 7ae1b4a - Implement P1766R1: diagnose giving non-C-compatible classes a typedef name for linkage purposes.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 7 11:47:48 PST 2020


Author: Richard Smith
Date: 2020-02-07T11:47:37-08:00
New Revision: 7ae1b4a0ce9c7f269cf3069e41496a78e3f28d49

URL: https://github.com/llvm/llvm-project/commit/7ae1b4a0ce9c7f269cf3069e41496a78e3f28d49
DIFF: https://github.com/llvm/llvm-project/commit/7ae1b4a0ce9c7f269cf3069e41496a78e3f28d49.diff

LOG: Implement P1766R1: diagnose giving non-C-compatible classes a typedef name for linkage purposes.

Summary:
Due to a recent (but retroactive) C++ rule change, only sufficiently
C-compatible classes are permitted to be given a typedef name for
linkage purposes. Add an enabled-by-default warning for these cases, and
rephrase our existing error for the case where we encounter the typedef
name for linkage after we've already computed and used a wrong linkage
in terms of the new rule.

Reviewers: rjmccall

Subscribers: cfe-commits

Tags: #clang

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

Added: 
    

Modified: 
    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

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 856effd19718..d24cd85673c6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -103,6 +103,43 @@ C11 Feature Support
 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
 ^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index bd5bfa3e122b..9de60d3a8d27 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -788,10 +788,27 @@ def ext_no_declarators : ExtWarn<"declaration does not declare anything">,
 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 err_typedef_changes_linkage : Error<
+  "unsupported: anonymous 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 +1813,13 @@ def note_nontrivial_objc_ownership : Note<
   "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<
@@ -8118,7 +8140,7 @@ def err_reference_to_local_in_enclosing_context : Error<
   "%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">;

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 41c5119fe573..ff0bd939613c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -4346,6 +4346,84 @@ void Sema::handleTagNumbering(const TagDecl *Tag, Scope *TagScope) {
   }
 }
 
+namespace {
+struct NonCLikeKind {
+  enum {
+    None,
+    BaseClass,
+    DefaultMemberInit,
+    Lambda,
+    Friend,
+    OtherMember,
+    Invalid,
+  } Kind = None;
+  SourceRange Range;
+
+  explicit operator bool() { return Kind != None; }
+};
+}
+
+/// Determine whether a class is C-like, according to the rules of C++
+/// [dcl.typedef] for anonymous classes with typedef names for linkage.
+static NonCLikeKind 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()) {
+      if (auto Kind = getNonCLikeKindForAnonymousStruct(MemberRD))
+        return Kind;
+    }
+  }
+
+  return {Invalid ? NonCLikeKind::Invalid : NonCLikeKind::None, {}};
+}
+
 void Sema::setTagNameForLinkagePurposes(TagDecl *TagFromDeclSpec,
                                         TypedefNameDecl *NewTD) {
   if (TagFromDeclSpec->isInvalidDecl())
@@ -4366,27 +4444,51 @@ void Sema::setTagNameForLinkagePurposes(TagDecl *TagFromDeclSpec,
     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].
+  //
+  // FIXME: Also diagnose if we've already computed the linkage. That ideally
+  // shouldn't happen, but there are constructs that the language rule doesn't
+  // disallow for which we can't reasonably avoid computing linkage early.
+  const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(TagFromDeclSpec);
+  NonCLikeKind NonCLike = RD ? getNonCLikeKindForAnonymousStruct(RD)
+                             : NonCLikeKind();
+  bool ChangesLinkage = TagFromDeclSpec->hasLinkageBeenComputed();
+  if (NonCLike || ChangesLinkage) {
+    if (NonCLike.Kind == NonCLikeKind::Invalid)
+      return;
 
-    SourceLocation tagLoc = TagFromDeclSpec->getInnerLocStart();
-    tagLoc = getLocForEndOfToken(tagLoc);
+    unsigned DiagID = diag::ext_non_c_like_anon_struct_in_typedef;
+    if (ChangesLinkage) {
+      // If the linkage changes, we can't accept this as an extension.
+      if (NonCLike.Kind == NonCLikeKind::None)
+        DiagID = diag::err_typedef_changes_linkage;
+      else
+        DiagID = diag::err_non_c_like_anon_struct_in_typedef;
+    }
 
-    llvm::SmallString<40> textToInsert;
-    textToInsert += ' ';
-    textToInsert += NewTD->getIdentifier()->getName();
-    Diag(tagLoc, diag::note_typedef_changes_linkage)
-        << FixItHint::CreateInsertion(tagLoc, textToInsert);
-    return;
+    SourceLocation FixitLoc =
+        getLocForEndOfToken(TagFromDeclSpec->getInnerLocStart());
+    llvm::SmallString<40> TextToInsert;
+    TextToInsert += ' ';
+    TextToInsert += NewTD->getIdentifier()->getName();
+
+    Diag(FixitLoc, DiagID)
+      << isa<TypeAliasDecl>(NewTD)
+      << FixItHint::CreateInsertion(FixitLoc, TextToInsert);
+    if (NonCLike.Kind != NonCLikeKind::None) {
+      Diag(NonCLike.Range.getBegin(), diag::note_non_c_like_anon_struct)
+        << NonCLike.Kind - 1 << NonCLike.Range;
+    }
+    Diag(NewTD->getLocation(), diag::note_typedef_for_linkage_here)
+      << NewTD << isa<TypeAliasDecl>(NewTD);
+
+    if (ChangesLinkage)
+      return;
   }
 
-  // Otherwise, set this is the anon-decl typedef for the tag.
+  // Otherwise, set this as the anon-decl typedef for the tag.
   TagFromDeclSpec->setTypedefNameForAnonDecl(NewTD);
 }
 
@@ -4917,6 +5019,10 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
     //   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 +6863,33 @@ NamedDecl *Sema::ActOnVariableDeclarator(
 
     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 +6897,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
             ? D.getName().TemplateId
             : nullptr,
         TemplateParamLists,
-        /*never a friend*/ false, IsMemberSpecialization, Invalid);
+        /*never a friend*/ false, IsMemberSpecialization, InvalidScope);
+    Invalid |= InvalidScope;
 
     if (TemplateParams) {
       if (!TemplateParams->size() &&

diff  --git a/clang/test/Analysis/inlining/eager-reclamation-path-notes.cpp b/clang/test/Analysis/inlining/eager-reclamation-path-notes.cpp
index 57c4a21fe8a6..c326150b0417 100644
--- a/clang/test/Analysis/inlining/eager-reclamation-path-notes.cpp
+++ b/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;

diff  --git a/clang/test/Analysis/padding_cpp.cpp b/clang/test/Analysis/padding_cpp.cpp
index ee49aea0c2be..f0e8beacda76 100644
--- a/clang/test/Analysis/padding_cpp.cpp
+++ b/clang/test/Analysis/padding_cpp.cpp
@@ -165,7 +165,7 @@ class Holder1 { // no-warning
   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;

diff  --git a/clang/test/Analysis/padding_message.cpp b/clang/test/Analysis/padding_message.cpp
index 4c7e06108154..e8b9f4022a77 100644
--- a/clang/test/Analysis/padding_message.cpp
+++ b/clang/test/Analysis/padding_message.cpp
@@ -265,13 +265,13 @@ class LotsOfSpace {
 };
 
 // expected-warning at +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 at +7{{\
 Excessive padding in 'TypedefSandwich2::NestedTypedef' (6 padding bytes, where 2 is optimal). \

diff  --git a/clang/test/CXX/class/class.local/p4.cpp b/clang/test/CXX/class/class.local/p4.cpp
index d7807440cc9c..2852d9627f42 100644
--- a/clang/test/CXX/class/class.local/p4.cpp
+++ b/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() { }
   };
 }

diff  --git a/clang/test/CXX/class/class.union/p2-0x.cpp b/clang/test/CXX/class/class.union/p2-0x.cpp
index 5fb8a671e31b..50a8022ffcf0 100644
--- a/clang/test/CXX/class/class.union/p2-0x.cpp
+++ b/clang/test/CXX/class/class.union/p2-0x.cpp
@@ -37,12 +37,12 @@ union U3 {
 
 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;
 };

diff  --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp
index d37ece6cb882..35fd15f0cc64 100644
--- a/clang/test/CXX/drs/dr4xx.cpp
+++ b/clang/test/CXX/drs/dr4xx.cpp
@@ -82,6 +82,9 @@ namespace dr406 { // dr406: yes
   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

diff  --git a/clang/test/Modules/submodules-merge-defs.cpp b/clang/test/Modules/submodules-merge-defs.cpp
index 0f7637de5732..9e1ac6ceef28 100644
--- a/clang/test/Modules/submodules-merge-defs.cpp
+++ b/clang/test/Modules/submodules-merge-defs.cpp
@@ -10,6 +10,8 @@
 #include "empty.h"
 #ifdef EARLY_INDIRECT_INCLUDE
 #include "indirect.h"
+// expected-warning at defs.h:28 3{{anonymous non-C-compatible type}}
+// expected-note at defs.h:28 6{{type is}}
 #endif
 
 A pre_a;

diff  --git a/clang/test/OpenMP/target_map_codegen.cpp b/clang/test/OpenMP/target_map_codegen.cpp
index cd1b5e49174e..b9766e82ce03 100644
--- a/clang/test/OpenMP/target_map_codegen.cpp
+++ b/clang/test/OpenMP/target_map_codegen.cpp
@@ -5165,7 +5165,7 @@ typedef struct {
   int *ptrBase1;
 } Base;
 
-typedef struct : public Base {
+typedef struct StructWithPtrTag : public Base {
   int *ptr;
   int *ptr2;
   int val;

diff  --git a/clang/test/SemaCXX/anonymous-struct.cpp b/clang/test/SemaCXX/anonymous-struct.cpp
index 42770030d2fa..017c867c95db 100644
--- a/clang/test/SemaCXX/anonymous-struct.cpp
+++ b/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,108 @@ struct E {
 };
 
 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 at -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 at -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 at -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}}
+
+typedef struct { // expected-warning {{anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here}}
+  friend void f() {} // expected-note {{type is not C-compatible due to this friend declaration}}
+} Friend; // expected-note {{type is given name 'Friend' 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}}
+  template<typename T> friend void f() {} // expected-note {{type is not C-compatible due to this friend declaration}}
+} FriendTemplate; // expected-note {{type is given name 'FriendTemplate' 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;
+
+// There are still some known permitted cases that require an early linkage
+// computation. Ensure we diagnose those too.
+namespace ValidButUnsupported {
+#if __cplusplus >= 201402L
+  template<typename T> auto compute_linkage() {
+    static int n;
+    return &n;
+  }
+
+  typedef struct { // expected-error {{unsupported: anonymous type given name for linkage purposes by typedef declaration after its linkage was computed; add a tag name here to establish linkage}}
+    struct X {};
+    decltype(compute_linkage<X>()) a;
+  } A; // expected-note {{by this typedef declaration}}
+#endif
+
+  // This fails in some language modes but not others.
+  template<typename T> struct Y {
+    static const int value = 10;
+  };
+  typedef struct { // expected-error 0-1{{unsupported}}
+    enum X {};
+    int arr[Y<X>::value];
+  } B; // expected-note 0-1{{by this typedef}}
+
+  template<typename T> void f() {}
+  typedef struct { // expected-error {{unsupported}}
+    enum X {};
+    int arr[&f<X> ? 1 : 2];
+  } C; // expected-note {{by this typedef}}
+}

diff  --git a/clang/test/SemaCXX/linkage.cpp b/clang/test/SemaCXX/linkage.cpp
index aa595948c696..6b5f2f75fb23 100644
--- a/clang/test/SemaCXX/linkage.cpp
+++ b/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
 

diff  --git a/clang/test/SemaCXX/linkage2.cpp b/clang/test/SemaCXX/linkage2.cpp
index 3cc9f61f0882..6fd0081450bf 100644
--- a/clang/test/SemaCXX/linkage2.cpp
+++ b/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 @@ namespace typedef_name_for_linkage {
     void f() { struct Inner {}; Use<Inner> ui; }
   } F;
 #if __cplusplus < 201103L
-  // expected-error at -2 {{unsupported: typedef changes linkage of anonymous type, but linkage was already computed}}
-  // expected-note at -5 {{use a tag name here}}
+  // expected-error at -4 {{given name for linkage purposes by typedef declaration after its linkage was computed}}
+  // expected-note at -4 {{due to this member}}
+  // expected-note at -4 {{by this typedef}}
 #endif
 }

diff  --git a/clang/test/SemaCXX/undefined-internal.cpp b/clang/test/SemaCXX/undefined-internal.cpp
index 60fa202384de..abbd0921fd7b 100644
--- a/clang/test/SemaCXX/undefined-internal.cpp
+++ b/clang/test/SemaCXX/undefined-internal.cpp
@@ -206,12 +206,12 @@ namespace OverloadUse {
 }
 
 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 {

diff  --git a/clang/test/SemaCXX/warn-unused-filescoped.cpp b/clang/test/SemaCXX/warn-unused-filescoped.cpp
index e052ecb1af99..7ea398feb2b1 100644
--- a/clang/test/SemaCXX/warn-unused-filescoped.cpp
+++ b/clang/test/SemaCXX/warn-unused-filescoped.cpp
@@ -164,9 +164,9 @@ namespace unused {
 }
 
 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'}}

diff  --git a/clang/test/SemaCXX/warn-unused-local-typedef.cpp b/clang/test/SemaCXX/warn-unused-local-typedef.cpp
index a9406531d2d7..7e893ba506a5 100644
--- a/clang/test/SemaCXX/warn-unused-local-typedef.cpp
+++ b/clang/test/SemaCXX/warn-unused-local-typedef.cpp
@@ -108,13 +108,13 @@ void template_fun_user() {
 }
 
 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;
 }

diff  --git a/clang/test/SemaTemplate/instantiate-function-2.cpp b/clang/test/SemaTemplate/instantiate-function-2.cpp
index ffdb4c914457..40d4a19cd081 100644
--- a/clang/test/SemaTemplate/instantiate-function-2.cpp
+++ b/clang/test/SemaTemplate/instantiate-function-2.cpp
@@ -49,11 +49,11 @@ namespace PR9654 {
 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 at -2 {{alias declarations are a C++11 extension}}
 #endif
-      T g() {
+      T g() { // expected-note {{}}
         return T();
       }
     };

diff  --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 606b8c956f16..171adbbae0eb 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -1091,7 +1091,7 @@ <h2 id="cxx20">C++2a implementation status</h2>
     </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>


        


More information about the cfe-commits mailing list