forked from OSchip/llvm-project
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:
parent
884acbb9e1
commit
7ae1b4a0ce
|
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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() &&
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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). \
|
||||
|
|
|
@ -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() { }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -5165,7 +5165,7 @@ typedef struct {
|
|||
int *ptrBase1;
|
||||
} Base;
|
||||
|
||||
typedef struct : public Base {
|
||||
typedef struct StructWithPtrTag : public Base {
|
||||
int *ptr;
|
||||
int *ptr2;
|
||||
int val;
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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'}}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue