diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/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 `. 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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -788,10 +788,27 @@ def ext_typedef_without_a_name : ExtWarn<"typedef requires a name">, InGroup; 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>; +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 @@ "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; def warn_cxx98_compat_static_data_member_in_union : Warning< @@ -8114,7 +8136,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">; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4346,6 +4346,84 @@ } } +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(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(D)) + return {NonCLikeKind::Friend, D->getSourceRange()}; + + // -- declare any members other than non-static data members, member + // enumerations, or member classes, + if (isa(D) || isa(D) || + isa(D)) + continue; + auto *MemberRD = dyn_cast(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 @@ 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(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(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(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 @@ // 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(Mem)) { // C++ [class.union]p3: // An anonymous union shall not have private or protected @@ -6757,28 +6863,33 @@ if (SC == SC_Static && CurContext->isRecord()) { if (const CXXRecordDecl *RD = dyn_cast(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 @@ ? 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 --- 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 --- a/clang/test/Analysis/padding_cpp.cpp +++ b/clang/test/Analysis/padding_cpp.cpp @@ -165,7 +165,7 @@ TemplateSandwich 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 --- a/clang/test/Analysis/padding_message.cpp +++ b/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). \ diff --git a/clang/test/CXX/class/class.local/p4.cpp b/clang/test/CXX/class/class.local/p4.cpp --- 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 --- a/clang/test/CXX/class/class.union/p2-0x.cpp +++ b/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; }; diff --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp --- a/clang/test/CXX/drs/dr4xx.cpp +++ b/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 diff --git a/clang/test/Modules/submodules-merge-defs.cpp b/clang/test/Modules/submodules-merge-defs.cpp --- 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@defs.h:28 3{{anonymous non-C-compatible type}} +// expected-note@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 --- a/clang/test/OpenMP/target_map_codegen.cpp +++ b/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; diff --git a/clang/test/SemaCXX/anonymous-struct.cpp b/clang/test/SemaCXX/anonymous-struct.cpp --- 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 @@ }; template 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 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 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 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 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()) a; + } A; // expected-note {{by this typedef declaration}} +#endif + + // This fails in some language modes but not others. + template struct Y { + static const int value = 10; + }; + typedef struct { // expected-error 0-1{{unsupported}} + enum X {}; + int arr[Y::value]; + } B; // expected-note 0-1{{by this typedef}} + + template void f() {} + typedef struct { // expected-error {{unsupported}} + enum X {}; + int arr[&f ? 1 : 2]; + } C; // expected-note {{by this typedef}} +} diff --git a/clang/test/SemaCXX/linkage.cpp b/clang/test/SemaCXX/linkage.cpp --- 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 --- 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 @@ void f() { struct Inner {}; Use 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 } diff --git a/clang/test/SemaCXX/undefined-internal.cpp b/clang/test/SemaCXX/undefined-internal.cpp --- a/clang/test/SemaCXX/undefined-internal.cpp +++ b/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 { diff --git a/clang/test/SemaCXX/warn-unused-filescoped.cpp b/clang/test/SemaCXX/warn-unused-filescoped.cpp --- a/clang/test/SemaCXX/warn-unused-filescoped.cpp +++ b/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'}} diff --git a/clang/test/SemaCXX/warn-unused-local-typedef.cpp b/clang/test/SemaCXX/warn-unused-local-typedef.cpp --- a/clang/test/SemaCXX/warn-unused-local-typedef.cpp +++ b/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; } diff --git a/clang/test/SemaTemplate/instantiate-function-2.cpp b/clang/test/SemaTemplate/instantiate-function-2.cpp --- a/clang/test/SemaTemplate/instantiate-function-2.cpp +++ b/clang/test/SemaTemplate/instantiate-function-2.cpp @@ -49,11 +49,11 @@ namespace AliasTagDef { template 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(); } }; diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1091,7 +1091,7 @@ P1766R1 (DR) - No + Clang 11 P1811R0