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
This commit is contained in:
Richard Smith 2020-02-05 18:52:38 -08:00
parent 884acbb9e1
commit 7ae1b4a0ce
19 changed files with 339 additions and 68 deletions

View File

@ -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
^^^^^^^^^^^^^^^^^^^^^

View File

@ -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">;

View File

@ -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() &&

View File

@ -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;

View File

@ -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;

View File

@ -265,13 +265,13 @@ class LotsOfSpace {
};
// 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). \

View File

@ -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() { }
};
}

View File

@ -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;
};

View File

@ -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

View File

@ -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;

View File

@ -5165,7 +5165,7 @@ typedef struct {
int *ptrBase1;
} Base;
typedef struct : public Base {
typedef struct StructWithPtrTag : public Base {
int *ptr;
int *ptr2;
int val;

View File

@ -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@-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}}
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}}
}

View File

@ -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

View File

@ -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@-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
}

View File

@ -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 {

View File

@ -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'}}

View File

@ -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;
}

View File

@ -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@-2 {{alias declarations are a C++11 extension}}
#endif
T g() {
T g() { // expected-note {{}}
return T();
}
};

View File

@ -1091,7 +1091,7 @@ as the draft C++2a standard evolves.
</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>