aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Basic/DiagnosticParseKinds.td4
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td5
-rw-r--r--lib/Parse/ParseDeclCXX.cpp26
-rw-r--r--lib/Sema/SemaDecl.cpp61
-rw-r--r--lib/Sema/SemaDeclCXX.cpp8
-rw-r--r--lib/Sema/SemaTemplate.cpp40
-rw-r--r--test/CXX/class/class.friend/p2.cpp4
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp3
-rw-r--r--test/CXX/temp/temp.decls/temp.friend/p3.cpp4
-rw-r--r--test/Parser/cxx-friend.cpp4
-rw-r--r--test/SemaTemplate/friend-template.cpp17
11 files changed, 130 insertions, 46 deletions
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index c018497907..6971df50cb 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -279,6 +279,10 @@ def err_expected_type_name_after_typename : Error<
def err_variadic_templates : Error<
"variadic templates are only allowed in C++0x">;
+
+// C++ declarations
+def err_friend_decl_defines_class : Error<
+ "cannot define a type in a friend declaration">;
// Language specific pragmas
// - Generic warnings
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 095b7ab387..44b582d43c 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -308,8 +308,6 @@ def err_static_assert_expression_is_not_constant : Error<
"static_assert expression is not an integral constant expression">;
def err_static_assert_failed : Error<"static_assert failed \"%0\"">;
-def err_friend_decl_defines_class : Error<
- "cannot define a type in a friend declaration">;
def err_unexpected_friend : Error<
"friends can only be classes or functions">;
def err_friend_is_member : Error<
@@ -317,8 +315,7 @@ def err_friend_is_member : Error<
def ext_friend_inner_class : Extension<
"C++ 98 does not allow inner classes as friends">;
def err_unelaborated_friend_type : Error<
- "must specify '%select{class|union}0' in a friend "
- "%select{class|union}0 declaration">;
+ "must specify '%select{struct|union|class|enum}0' to befriend %1">;
def err_qualified_friend_not_found : Error<
"no function named %0 with type %1 was found in the specified scope">;
def err_introducing_special_friend : Error<
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 59a6e6281c..bcf332bf30 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -624,9 +624,22 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// 'struct foo :...' then this is a definition. Otherwise we have
// something like 'struct foo xyz', a reference.
Action::TagUseKind TUK;
- if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon)))
- TUK = Action::TUK_Definition;
- else if (Tok.is(tok::semi))
+ if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon))) {
+ if (DS.isFriendSpecified()) {
+ // C++ [class.friend]p2:
+ // A class shall not be defined in a friend declaration.
+ Diag(Tok.getLocation(), diag::err_friend_decl_defines_class)
+ << SourceRange(DS.getFriendSpecLoc());
+
+ // Skip everything up to the semicolon, so that this looks like a proper
+ // friend class (or template thereof) declaration.
+ SkipUntil(tok::semi, true, true);
+ TUK = Action::TUK_Friend;
+ } else {
+ // Okay, this is a class definition.
+ TUK = Action::TUK_Definition;
+ }
+ } else if (Tok.is(tok::semi))
TUK = DS.isFriendSpecified() ? Action::TUK_Friend : Action::TUK_Declaration;
else
TUK = Action::TUK_Reference;
@@ -1043,12 +1056,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
if (Tok.is(tok::semi)) {
ConsumeToken();
-
- if (DS.isFriendSpecified()) {
- Actions.ActOnFriendTypeDecl(CurScope, DS, move(TemplateParams));
- } else
- Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
-
+ Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
return;
}
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 1f53cde64b..f8751fb9c9 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1288,6 +1288,59 @@ Sema::DeclPtrTy Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
Tag = dyn_cast<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
}
+ if (DS.isFriendSpecified()) {
+ // We have a "friend" declaration that does not have a declarator.
+ // Look at the type to see if the friend declaration was handled
+ // elsewhere (e.g., for friend classes and friend class templates).
+ // If not, produce a suitable diagnostic or go try to befriend the
+ // type itself.
+ QualType T;
+ if (DS.getTypeSpecType() == DeclSpec::TST_typename ||
+ DS.getTypeSpecType() == DeclSpec::TST_typeofType)
+ T = QualType::getFromOpaquePtr(DS.getTypeRep());
+ else if (DS.getTypeSpecType() == DeclSpec::TST_typeofExpr ||
+ DS.getTypeSpecType() == DeclSpec::TST_decltype)
+ T = ((Expr *)DS.getTypeRep())->getType();
+ else if (DS.getTypeSpecType() == DeclSpec::TST_class ||
+ DS.getTypeSpecType() == DeclSpec::TST_struct ||
+ DS.getTypeSpecType() == DeclSpec::TST_union)
+ return DeclPtrTy::make(Tag);
+
+ if (T.isNull()) {
+ // Fall through to diagnose this error, below.
+ } else if (const RecordType *RecordT = T->getAs<RecordType>()) {
+ // C++ [class.friend]p2:
+ // An elaborated-type-specifier shall be used in a friend declaration
+ // for a class.
+
+ // We have something like "friend C;", where C is the name of a
+ // class type but is missing an elaborated type specifier. Complain,
+ // but tell the user exactly how to fix the problem.
+ RecordDecl *RecordD = RecordT->getDecl();
+ Diag(DS.getTypeSpecTypeLoc(), diag::err_unelaborated_friend_type)
+ << (unsigned)RecordD->getTagKind()
+ << QualType(RecordT, 0)
+ << SourceRange(DS.getFriendSpecLoc())
+ << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
+ RecordD->getKindName() + std::string(" "));
+
+ // FIXME: We could go into ActOnTag to actually make the friend
+ // declaration happen at this point.
+ return DeclPtrTy();
+ }
+
+ if (!T.isNull() && T->isDependentType()) {
+ // Since T is a dependent type, handle it as a friend type
+ // declaration.
+ return ActOnFriendTypeDecl(S, DS, MultiTemplateParamsArg(*this, 0, 0));
+ }
+
+ // Complain about any non-dependent friend type here.
+ Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
+ << DS.getSourceRange();
+ return DeclPtrTy();
+ }
+
if (RecordDecl *Record = dyn_cast_or_null<RecordDecl>(Tag)) {
if (!Record->getDeclName() && Record->isDefinition() &&
DS.getStorageClassSpec() != DeclSpec::SCS_typedef) {
@@ -1305,7 +1358,7 @@ Sema::DeclPtrTy Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
if (Record->getDeclName() && getLangOptions().Microsoft)
return DeclPtrTy::make(Tag);
}
-
+
if (!DS.isMissingDeclaratorOk() &&
DS.getTypeSpecType() != DeclSpec::TST_error) {
// Warn about typedefs of enums without names, since this is an
@@ -4022,11 +4075,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
= MatchTemplateParametersToScopeSpecifier(KWLoc, SS,
(TemplateParameterList**)TemplateParameterLists.get(),
TemplateParameterLists.size())) {
- if (TUK == TUK_Friend) {
- // When declaring a friend template, we do want to match the
- // template parameters to the scope specifier, but don't go so far
- // as to try to declare a new template.
- } else if (TemplateParams->size() > 0) {
+ if (TemplateParams->size() > 0) {
// This is a declaration or definition of a class template (which may
// be a member of another template).
OwnedDecl = false;
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 390a8048db..02c1a08c8f 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -4141,14 +4141,6 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
break;
}
- // C++ [class.friend]p2: A class shall not be defined inside
- // a friend declaration.
- if (IsDefinition) {
- Diag(DS.getFriendSpecLoc(), diag::err_friend_decl_defines_class)
- << DS.getSourceRange();
- return DeclPtrTy();
- }
-
// C++98 [class.friend]p1: A friend of a class is a function
// or class that is not a member of the class . . .
// But that's a silly restriction which nobody implements for
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index d5fb7e82a8..72274525fd 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -579,6 +579,18 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
Previous = LookupQualifiedName(SemanticContext, Name, LookupOrdinaryName,
true);
+ } else if (TUK == TUK_Friend) {
+ // C++ [namespace.memdef]p3:
+ // [...] When looking for a prior declaration of a class or a function
+ // declared as a friend, and when the name of the friend class or
+ // function is neither a qualified name nor a template-id, scopes outside
+ // the innermost enclosing namespace scope are not considered.
+ SemanticContext = CurContext;
+ while (!SemanticContext->isFileContext())
+ SemanticContext = SemanticContext->getLookupParent();
+
+ Previous = LookupQualifiedName(SemanticContext, Name, LookupOrdinaryName,
+ true);
} else {
SemanticContext = CurContext;
Previous = LookupName(S, Name, LookupOrdinaryName, true);
@@ -654,13 +666,6 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
// FIXME: If we had a scope specifier, we better have a previous template
// declaration!
- // If this is a friend declaration of an undeclared template,
- // create the template in the innermost namespace scope.
- if (TUK == TUK_Friend && !PrevClassTemplate) {
- while (!SemanticContext->isFileContext())
- SemanticContext = SemanticContext->getParent();
- }
-
CXXRecordDecl *NewClass =
CXXRecordDecl::Create(Context, Kind, SemanticContext, NameLoc, Name, KWLoc,
PrevClassTemplate?
@@ -682,10 +687,7 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
(void)T;
// Set the access specifier.
- if (TUK == TUK_Friend)
- NewTemplate->setObjectOfFriendDecl(/* PreviouslyDeclared = */
- PrevClassTemplate != NULL);
- else
+ if (!Invalid && TUK != TUK_Friend)
SetMemberAccessSpecifier(NewTemplate, PrevClassTemplate, AS);
// Set the lexical context of these templates
@@ -701,11 +703,14 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
if (TUK != TUK_Friend)
PushOnScopeChains(NewTemplate, S);
else {
- // We might be replacing an existing declaration in the lookup tables;
- // if so, borrow its access specifier.
- if (PrevClassTemplate)
+ if (PrevClassTemplate && PrevClassTemplate->getAccess() != AS_none) {
NewTemplate->setAccess(PrevClassTemplate->getAccess());
+ NewClass->setAccess(PrevClassTemplate->getAccess());
+ }
+ NewTemplate->setObjectOfFriendDecl(/* PreviouslyDeclared = */
+ PrevClassTemplate != NULL);
+
// Friend templates are visible in fairly strange ways.
if (!CurContext->isDependentContext()) {
DeclContext *DC = SemanticContext->getLookupContext();
@@ -714,6 +719,13 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
PushOnScopeChains(NewTemplate, EnclosingScope,
/* AddToContext = */ false);
}
+
+ FriendDecl *Friend = FriendDecl::Create(Context, CurContext,
+ NewClass->getLocation(),
+ NewTemplate,
+ /*FIXME:*/NewClass->getLocation());
+ Friend->setAccess(AS_public);
+ CurContext->addDecl(Friend);
}
if (Invalid) {
diff --git a/test/CXX/class/class.friend/p2.cpp b/test/CXX/class/class.friend/p2.cpp
index f130beb7d2..98be2049e7 100644
--- a/test/CXX/class/class.friend/p2.cpp
+++ b/test/CXX/class/class.friend/p2.cpp
@@ -1,8 +1,10 @@
// RUN: clang-cc -fsyntax-only -verify %s
+struct B0;
+
class A {
friend class B {}; // expected-error {{cannot define a type in a friend declaration}}
friend int; // expected-error {{friends can only be classes or functions}}
- friend B; // expected-error {{must specify 'class' in a friend class declaration}}
+ friend B0; // expected-error {{must specify 'struct' to befriend}}
friend class C; // okay
};
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp
index 89f938d1f7..657cf20cf3 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp
@@ -13,7 +13,8 @@ class A1 {
friend union A; // expected-error {{use of 'A' with tag type that does not match previous declaration}}
// FIXME: a better error would be something like 'enum types cannot be friends'
- friend enum A; // expected-error {{ISO C++ forbids forward references to 'enum' types}}
+ friend enum A; // expected-error {{ISO C++ forbids forward references to 'enum' types}} \
+ // expected-error{{classes or functions}}
};
template <class T> struct B { // expected-note {{previous use is here}}
diff --git a/test/CXX/temp/temp.decls/temp.friend/p3.cpp b/test/CXX/temp/temp.decls/temp.friend/p3.cpp
index dd856e9cc3..4615bebe71 100644
--- a/test/CXX/temp/temp.decls/temp.friend/p3.cpp
+++ b/test/CXX/temp/temp.decls/temp.friend/p3.cpp
@@ -7,5 +7,7 @@ template <class T> class A {
class B {
template <class T> friend class A;
template <class T> friend class Undeclared;
- template <class T> friend typename A<T>::Member; // expected-error {{friend type templates must use an elaborated type}}
+
+ // FIXME: Diagnostic below could be (and was) better.
+ template <class T> friend typename A<T>::Member; // expected-error {{classes or functions}}
};
diff --git a/test/Parser/cxx-friend.cpp b/test/Parser/cxx-friend.cpp
index 397894727b..14b31af761 100644
--- a/test/Parser/cxx-friend.cpp
+++ b/test/Parser/cxx-friend.cpp
@@ -21,8 +21,8 @@ class B {
// 'A' here should refer to the declaration above.
friend class A;
- friend C; // expected-error {{must specify 'class' in a friend class declaration}}
- friend U; // expected-error {{must specify 'union' in a friend union declaration}}
+ friend C; // expected-error {{must specify 'class' to befriend}}
+ friend U; // expected-error {{must specify 'union' to befriend}}
friend int; // expected-error {{friends can only be classes or functions}}
friend void myfunc();
diff --git a/test/SemaTemplate/friend-template.cpp b/test/SemaTemplate/friend-template.cpp
new file mode 100644
index 0000000000..6268cd8907
--- /dev/null
+++ b/test/SemaTemplate/friend-template.cpp
@@ -0,0 +1,17 @@
+// RUN: clang-cc -fsyntax-only %s
+
+// PR5057
+namespace std {
+ class X {
+ public:
+ template<typename T>
+ friend struct Y;
+ };
+}
+
+namespace std {
+ template<typename T>
+ struct Y
+ {
+ };
+}