First pass at friend semantics.

llvm-svn: 78274
This commit is contained in:
John McCall 2009-08-06 02:15:43 +00:00
parent 51eeb36c8c
commit 07e91c04ba
17 changed files with 439 additions and 57 deletions

View File

@ -35,6 +35,7 @@ def err_invalid_short_spec : Error<"'short %0' is invalid">;
def err_invalid_long_spec : Error<"'long %0' is invalid">;
def err_invalid_longlong_spec : Error<"'long long %0' is invalid">;
def err_invalid_complex_spec : Error<"'_Complex %0' is invalid">;
def err_friend_storage_spec : Error<"'%0' is invalid in friend declarations">;
def ext_ident_list_in_param : Extension<
"type-less parameter names in function declaration">;
@ -152,6 +153,8 @@ def err_typename_invalid_functionspec : Error<
"type name does not allow function specifier to be specified">;
def err_invalid_decl_spec_combination : Error<
"cannot combine with previous '%0' declaration specifier">;
def err_friend_invalid_in_context : Error<
"'friend' used outside of class">;
def err_unknown_typename : Error<
"unknown type name %0">;
def err_use_of_tag_name_without_tag : Error<

View File

@ -293,8 +293,20 @@ 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_outside_class : Error<
"'friend' used outside of class">;
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<
"friends cannot be members of the declaring class">;
def err_unelaborated_friend_type : Error<
"must specify '%select{class|union}0' in a friend "
"%select{class|union}0 declaration">;
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<
"must use a qualified name when declaring a %select{constructor|"
"destructor|conversion operator}0 as a friend">;
def err_abstract_type_in_decl : Error<
"%select{return|parameter|variable|field}1 type %0 is an abstract class">;

View File

@ -22,6 +22,7 @@
#include "clang/Parse/DeclSpec.h"
#include "clang/Parse/Ownership.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/ADT/PointerUnion.h"
namespace clang {
// Semantic.
@ -413,7 +414,8 @@ public:
enum TagUseKind {
TUK_Reference, // Reference to a tag: 'struct foo *X;'
TUK_Declaration, // Fwd decl of a tag: 'struct foo;'
TUK_Definition // Definition of a tag: 'struct foo { int X; } Y;'
TUK_Definition, // Definition of a tag: 'struct foo { int X; } Y;'
TUK_Friend // Friend declaration: 'friend struct foo;'
};
/// \brief The parser has encountered a tag (e.g., "class X") that should be
@ -1109,12 +1111,11 @@ public:
}
/// ActOnFriendDecl - This action is called when a friend declaration is
/// encountered. Returns false on success.
virtual bool ActOnFriendDecl(Scope *S, SourceLocation FriendLoc,
DeclPtrTy Dcl) {
return false;
/// encountered.
virtual DeclPtrTy ActOnFriendDecl(Scope *S,
llvm::PointerUnion<const DeclSpec*,Declarator*> D) {
return DeclPtrTy();
}
//===------------------------- C++ Expressions --------------------------===//

View File

@ -906,6 +906,14 @@ private:
//===--------------------------------------------------------------------===//
// C99 6.7: Declarations.
/// A context for parsing declaration specifiers. TODO: flesh this
/// out, there are other significant restrictions on specifiers than
/// would be best implemented in the parser.
enum DeclSpecContext {
DSC_normal, // normal context
DSC_class // class context, enables 'friend'
};
DeclGroupPtrTy ParseDeclaration(unsigned Context, SourceLocation &DeclEnd);
DeclGroupPtrTy ParseSimpleDeclaration(unsigned Context,
@ -922,7 +930,8 @@ private:
AccessSpecifier AS);
void ParseDeclarationSpecifiers(DeclSpec &DS,
const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
AccessSpecifier AS = AS_none);
AccessSpecifier AS = AS_none,
DeclSpecContext DSC = DSC_normal);
bool ParseOptionalTypeSpecifier(DeclSpec &DS, bool &isInvalid,
const char *&PrevSpec,
unsigned &DiagID,

View File

@ -398,6 +398,24 @@ void DeclSpec::Finish(Diagnostic &D, Preprocessor &PP) {
}
}
// C++ [class.friend]p6:
// No storage-class-specifier shall appear in the decl-specifier-seq
// of a friend declaration.
if (isFriendSpecified() && getStorageClassSpec()) {
DeclSpec::SCS SC = getStorageClassSpec();
const char *SpecName = getSpecifierName(SC);
SourceLocation SCLoc = getStorageClassSpecLoc();
SourceLocation SCEndLoc = SCLoc.getFileLocWithOffset(strlen(SpecName));
Diag(D, SCLoc, SrcMgr, diag::err_friend_storage_spec)
<< SpecName
<< CodeModificationHint::CreateRemoval(SourceRange(SCLoc, SCEndLoc));
ClearStorageClassSpecs();
}
// Okay, now we can infer the real type.
// TODO: return "auto function" and other bad things based on the real type.

View File

@ -27,7 +27,11 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) {
assert((Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try)) &&
"Current token not a '{', ':' or 'try'!");
DeclPtrTy FnD = Actions.ActOnCXXMemberDeclarator(CurScope, AS, D, 0, 0);
DeclPtrTy FnD;
if (D.getDeclSpec().isFriendSpecified())
FnD = Actions.ActOnFriendDecl(CurScope, &D);
else
FnD = Actions.ActOnCXXMemberDeclarator(CurScope, AS, D, 0, 0);
HandleMemberFunctionDefaultArgs(D, FnD);

View File

@ -708,7 +708,8 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
///
void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
const ParsedTemplateInfo &TemplateInfo,
AccessSpecifier AS) {
AccessSpecifier AS,
DeclSpecContext DSContext) {
DS.SetRangeStart(Tok.getLocation());
while (1) {
bool isInvalid = false;
@ -968,7 +969,13 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// friend
case tok::kw_friend:
isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
if (DSContext == DSC_class)
isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
else {
PrevSpec = ""; // not actually used by the diagnostic
DiagID = diag::err_friend_invalid_in_context;
isInvalid = true;
}
break;
// type-specifier

View File

@ -567,15 +567,16 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
}
}
// There are three options here. If we have 'struct foo;', then
// this is a forward declaration. If we have 'struct foo {...' or
// There are four options here. If we have 'struct foo;', then this
// is either a forward declaration or a friend declaration, which
// have to be treated differently. If we have 'struct foo {...' or
// '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) && !DS.isFriendSpecified())
TUK = Action::TUK_Declaration;
else if (Tok.is(tok::semi))
TUK = DS.isFriendSpecified() ? Action::TUK_Friend : Action::TUK_Declaration;
else
TUK = Action::TUK_Reference;
@ -600,7 +601,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// to turn that template-id into a type.
bool Owned = false;
if (TemplateId && TUK != Action::TUK_Reference) {
if (TemplateId && TUK != Action::TUK_Reference && TUK != Action::TUK_Friend) {
// Explicit specialization, class template partial specialization,
// or explicit instantiation.
ASTTemplateArgsPtr TemplateArgsPtr(Actions,
@ -727,10 +728,6 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
if (DS.SetTypeSpecType(TagType, StartLoc, PrevSpec, DiagID,
TagOrTempResult.get().getAs<void>(), Owned))
Diag(StartLoc, DiagID) << PrevSpec;
if (DS.isFriendSpecified())
Actions.ActOnFriendDecl(CurScope, DS.getFriendSpecLoc(),
TagOrTempResult.get());
}
/// ParseBaseClause - Parse the base-clause of a C++ class [C++ class.derived].
@ -951,24 +948,17 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) {
// decl-specifier-seq:
// Parse the common declaration-specifiers piece.
DeclSpec DS;
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS);
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_class);
if (Tok.is(tok::semi)) {
ConsumeToken();
// C++ 9.2p7: The member-declarator-list can be omitted only after a
// class-specifier or an enum-specifier or in a friend declaration.
// FIXME: Friend declarations.
switch (DS.getTypeSpecType()) {
case DeclSpec::TST_struct:
case DeclSpec::TST_union:
case DeclSpec::TST_class:
case DeclSpec::TST_enum:
if (DS.isFriendSpecified())
Actions.ActOnFriendDecl(CurScope, &DS);
else
Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
return;
default:
Diag(DSStart, diag::err_no_declarators);
return;
}
return;
}
Declarator DeclaratorInfo(DS, Declarator::MemberContext);
@ -1066,11 +1056,17 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) {
// NOTE: If Sema is the Action module and declarator is an instance field,
// this call will *not* return the created decl; It will return null.
// See Sema::ActOnCXXMemberDeclarator for details.
DeclPtrTy ThisDecl = Actions.ActOnCXXMemberDeclarator(CurScope, AS,
DeclaratorInfo,
BitfieldSize.release(),
Init.release(),
Deleted);
DeclPtrTy ThisDecl;
if (DS.isFriendSpecified()) {
// TODO: handle initializers, bitfields, 'delete'
ThisDecl = Actions.ActOnFriendDecl(CurScope, &DeclaratorInfo);
} else
ThisDecl = Actions.ActOnCXXMemberDeclarator(CurScope, AS,
DeclaratorInfo,
BitfieldSize.release(),
Init.release(),
Deleted);
if (ThisDecl)
DeclsInGroup.push_back(ThisDecl);

View File

@ -1192,6 +1192,9 @@ public:
LookupResult LookupQualifiedName(DeclContext *LookupCtx, DeclarationName Name,
LookupNameKind NameKind,
bool RedeclarationOnly = false);
Decl *LookupQualifiedNameWithType(DeclContext *LookupCtx,
DeclarationName Name,
QualType T);
LookupResult LookupParsedName(Scope *S, const CXXScopeSpec *SS,
DeclarationName Name,
LookupNameKind NameKind,
@ -2001,8 +2004,8 @@ public:
ExprArg AssertExpr,
ExprArg AssertMessageExpr);
virtual bool ActOnFriendDecl(Scope *S, SourceLocation FriendLoc,
DeclPtrTy Dcl);
virtual DeclPtrTy ActOnFriendDecl(Scope *S,
llvm::PointerUnion<const DeclSpec*,Declarator*> D);
QualType CheckConstructorDeclarator(Declarator &D, QualType R,
FunctionDecl::StorageClass& SC);

View File

@ -1275,6 +1275,9 @@ Sema::DeclPtrTy Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
if (!DS.getTypeRep()) // We probably had an error
return DeclPtrTy();
// Note that the above type specs guarantee that the
// type rep is a Decl, whereas in many of the others
// it's a Type.
Tag = dyn_cast<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
}
@ -3973,7 +3976,8 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
// If this is a use of a previous tag, or if the tag is already declared
// in the same scope (so that the definition/declaration completes or
// rementions the tag), reuse the decl.
if (TUK == TUK_Reference || isDeclInScope(PrevDecl, SearchDC, S)) {
if (TUK == TUK_Reference || TUK == TUK_Friend ||
isDeclInScope(PrevDecl, SearchDC, S)) {
// Make sure that this wasn't declared as an enum and now used as a
// struct or something similar.
if (!isAcceptableTagRedeclaration(PrevTagDecl, Kind, KWLoc, *Name)) {
@ -4009,6 +4013,11 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
if (TUK == TUK_Reference)
return DeclPtrTy::make(PrevDecl);
// If this is a friend, make sure we create the new
// declaration in the appropriate semantic context.
if (TUK == TUK_Friend)
SearchDC = PrevDecl->getDeclContext();
// Diagnose attempts to redefine a tag.
if (TUK == TUK_Definition) {
if (TagDecl *Def = PrevTagDecl->getDefinition(Context)) {
@ -4039,7 +4048,8 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
}
}
// If we get here we have (another) forward declaration or we
// have a definition. Just create a new decl.
// have a definition. Just create a new decl.
} else {
// If we get here, this is a definition of a new tag type in a nested
// scope, e.g. "struct foo; void bar() { struct foo; }", just create a
@ -4102,6 +4112,18 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
(S->getEntity() &&
((DeclContext *)S->getEntity())->isTransparentContext()))
S = S->getParent();
} else if (TUK == TUK_Friend && SS.isEmpty() && Name) {
// C++ [namespace.memdef]p3:
// If a friend declaration in a non-local class first declares a
// class or function, the friend class or function is a member of
// the innermost enclosing namespace.
while (!SearchDC->isNamespace() && !SearchDC->isTranslationUnit())
SearchDC = SearchDC->getParent();
// The entity of a decl scope is a DeclContext; see PushDeclContext.
while (S->getEntity() != SearchDC)
S = S->getParent();
}
CreateNewDecl:
@ -4195,14 +4217,14 @@ CreateNewDecl:
New->setLexicalDeclContext(CurContext);
// Set the access specifier.
if (!Invalid)
if (!Invalid && TUK != TUK_Friend)
SetMemberAccessSpecifier(New, PrevDecl, AS);
if (TUK == TUK_Definition)
New->startDefinition();
// If this has an identifier, add it to the scope stack.
if (Name) {
if (Name && TUK != TUK_Friend) {
S = getNonFieldDeclScope(S);
PushOnScopeChains(New, S);
} else {

View File

@ -550,6 +550,8 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
bool isFunc = D.isFunctionDeclarator();
assert(!DS.isFriendSpecified());
// C++ 9.2p6: A member shall not be declared to have automatic storage
// duration (auto, register) or with the extern storage-class-specifier.
// C++ 7.1.1p8: The mutable specifier can be applied only to names of class
@ -3302,13 +3304,208 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertDeclaration(SourceLocation AssertLoc,
return DeclPtrTy::make(Decl);
}
bool Sema::ActOnFriendDecl(Scope *S, SourceLocation FriendLoc, DeclPtrTy Dcl) {
if (!(S->getFlags() & Scope::ClassScope)) {
Diag(FriendLoc, diag::err_friend_decl_outside_class);
return true;
Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S,
llvm::PointerUnion<const DeclSpec*,Declarator*> DU) {
Declarator *D = DU.dyn_cast<Declarator*>();
const DeclSpec &DS = (D ? D->getDeclSpec() : *DU.get<const DeclSpec*>());
assert(DS.isFriendSpecified());
assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
// If there's no declarator, then this can only be a friend class
// declaration (or else it's just invalid).
if (!D) {
// C++ [class.friend]p2:
// An elaborated-type-specifier shall be used in a friend declaration
// for a class.*
// * The class-key of the elaborated-type-specifier is required.
CXXRecordDecl *RD = 0;
switch (DS.getTypeSpecType()) {
case DeclSpec::TST_class:
case DeclSpec::TST_struct:
case DeclSpec::TST_union:
RD = dyn_cast_or_null<CXXRecordDecl>(static_cast<Decl*>(DS.getTypeRep()));
if (!RD) return DeclPtrTy();
break;
case DeclSpec::TST_typename:
if (const RecordType *RT =
((const Type*) DS.getTypeRep())->getAs<RecordType>())
RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
// fallthrough
default:
if (RD) {
Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type)
<< (RD->isUnion())
<< CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
RD->isUnion() ? " union" : " class");
return DeclPtrTy::make(RD);
}
Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
<< DS.getSourceRange();
return DeclPtrTy();
}
// The record declaration we get from friend declarations is not
// canonicalized; see ActOnTag.
assert(RD);
// C++ [class.friend]p2: A class shall not be defined inside
// a friend declaration.
if (RD->isDefinition())
Diag(DS.getFriendSpecLoc(), diag::err_friend_decl_defines_class)
<< RD->getSourceRange();
// C++ [class.friend]p1: A friend of a class is a function or
// class that is not a member of the class . . .
// Definitions currently get treated in a way that causes this
// error, so only report it if we didn't see a definition.
else if (RD->getDeclContext() == CurContext)
Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
return DeclPtrTy::make(RD);
}
return false;
// We have a declarator.
assert(D);
SourceLocation Loc = D->getIdentifierLoc();
QualType T = GetTypeForDeclarator(*D, S);
// C++ [class.friend]p1
// A friend of a class is a function or class....
// Note that this sees through typedefs, which is intended.
if (!T->isFunctionType()) {
Diag(Loc, diag::err_unexpected_friend);
// It might be worthwhile to try to recover by creating an
// appropriate declaration.
return DeclPtrTy();
}
// C++ [namespace.memdef]p3
// - If a friend declaration in a non-local class first declares a
// class or function, the friend class or function is a member
// of the innermost enclosing namespace.
// - The name of the friend is not found by simple name lookup
// until a matching declaration is provided in that namespace
// scope (either before or after the class declaration granting
// friendship).
// - If a friend function is called, its name may be found by the
// name lookup that considers functions from namespaces and
// classes associated with the types of the function arguments.
// - When looking for a prior declaration of a class or a function
// declared as a friend, scopes outside the innermost enclosing
// namespace scope are not considered.
CXXScopeSpec &ScopeQual = D->getCXXScopeSpec();
DeclarationName Name = GetNameForDeclarator(*D);
assert(Name);
// The existing declaration we found.
FunctionDecl *FD = NULL;
// The context we found the declaration in, or in which we should
// create the declaration.
DeclContext *DC;
// FIXME: handle local classes
// Recover from invalid scope qualifiers as if they just weren't there.
if (!ScopeQual.isInvalid() && ScopeQual.isSet()) {
DC = computeDeclContext(ScopeQual);
// FIXME: handle dependent contexts
if (!DC) return DeclPtrTy();
Decl *Dec = LookupQualifiedNameWithType(DC, Name, T);
// If searching in that context implicitly found a declaration in
// a different context, treat it like it wasn't found at all.
// TODO: better diagnostics for this case. Suggesting the right
// qualified scope would be nice...
if (!Dec || Dec->getDeclContext() != DC) {
D->setInvalidType();
Diag(Loc, diag::err_qualified_friend_not_found) << Name << T;
return DeclPtrTy();
}
// C++ [class.friend]p1: A friend of a class is a function or
// class that is not a member of the class . . .
if (DC == CurContext)
Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
FD = cast<FunctionDecl>(Dec);
// Otherwise walk out to the nearest namespace scope looking for matches.
} else {
// TODO: handle local class contexts.
DC = CurContext;
while (true) {
// Skip class contexts. If someone can cite chapter and verse
// for this behavior, that would be nice --- it's what GCC and
// EDG do, and it seems like a reasonable intent, but the spec
// really only says that checks for unqualified existing
// declarations should stop at the nearest enclosing namespace,
// not that they should only consider the nearest enclosing
// namespace.
while (DC->isRecord()) DC = DC->getParent();
Decl *Dec = LookupQualifiedNameWithType(DC, Name, T);
// TODO: decide what we think about using declarations.
if (Dec) {
FD = cast<FunctionDecl>(Dec);
break;
}
if (DC->isFileContext()) break;
DC = DC->getParent();
}
// C++ [class.friend]p1: A friend of a class is a function or
// class that is not a member of the class . . .
if (FD && DC == CurContext)
Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
}
// If we didn't find something matching the type exactly, create
// a declaration. This declaration should only be findable via
// argument-dependent lookup.
if (!FD) {
assert(DC->isFileContext());
// This implies that it has to be an operator or function.
if (D->getKind() == Declarator::DK_Constructor ||
D->getKind() == Declarator::DK_Destructor ||
D->getKind() == Declarator::DK_Conversion) {
Diag(Loc, diag::err_introducing_special_friend) <<
(D->getKind() == Declarator::DK_Constructor ? 0 :
D->getKind() == Declarator::DK_Destructor ? 1 : 2);
return DeclPtrTy();
}
bool Redeclaration = false;
NamedDecl *ND = ActOnFunctionDeclarator(S, *D, DC, T,
/* PrevDecl = */ NULL,
MultiTemplateParamsArg(*this),
/* isFunctionDef */ false,
Redeclaration);
FD = cast_or_null<FunctionDecl>(ND);
// Note that we're creating a declaration but *not* pushing
// it onto the scope chains.
// TODO: make accessible via argument-dependent lookup.
}
// TODO: actually register the function as a friend.
return DeclPtrTy::make(FD);
}
void Sema::SetDeclDeleted(DeclPtrTy dcl, SourceLocation DelLoc) {

View File

@ -1677,6 +1677,26 @@ ObjCCategoryImplDecl *Sema::LookupObjCCategoryImpl(IdentifierInfo *II) {
return cast_or_null<ObjCCategoryImplDecl>(D);
}
// Attempts to find a declaration in the given declaration context
// with exactly the given type. Returns null if no such declaration
// was found.
Decl *Sema::LookupQualifiedNameWithType(DeclContext *DC,
DeclarationName Name,
QualType T) {
LookupResult result =
LookupQualifiedName(DC, Name, LookupOrdinaryName, true);
CanQualType CQT = Context.getCanonicalType(T);
for (LookupResult::iterator ir = result.begin(), ie = result.end();
ir != ie; ++ir)
if (FunctionDecl *CurFD = dyn_cast<FunctionDecl>(*ir))
if (Context.getCanonicalType(CurFD->getType()) == CQT)
return CurFD;
return NULL;
}
void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S,
QualType T1, QualType T2,
FunctionSet &Functions) {

View File

@ -1,9 +1,7 @@
// RUN: clang-cc -fsyntax-only -verify %s
// XFAIL
// FIXME: This part is here to demonstrate the failure in looking up 'f', it can
// be removed once the whole test passes.
typedef int f;
namespace N0 {
struct A {
friend void f();

View File

@ -0,0 +1,59 @@
// RUN: clang-cc -fsyntax-only -verify %s
struct Outer {
struct Inner {
int intfield;
};
};
struct Base {
void base_member();
typedef int Int;
Int typedeffed_member();
};
struct Derived : public Base {
};
int myglobal;
class A {
class AInner {
};
friend class PreDeclared;
friend class Outer::Inner;
friend int Outer::Inner::intfield; // expected-error {{ friends can only be classes or functions }}
friend int Outer::Inner::missing_field; //expected-error {{ friends can only be classes or functions }}
friend int myoperation(float); // okay
friend int myglobal; // expected-error {{ friends can only be classes or functions }}
void a_member();
friend void A::a_member(); // expected-error {{ friends cannot be members of the declaring class }}
friend void a_member(); // okay (because we ignore class scopes when looking up friends)
friend class A::AInner; // expected-error {{ friends cannot be members of the declaring class }}
friend class AInner; // expected-error {{ friends cannot be members of the declaring class }}
friend void Derived::missing_member(); // expected-error {{ no function named 'missing_member' with type 'void ()' was found in the specified scope }}
friend void Derived::base_member(); // expected-error {{ no function named 'base_member' with type 'void ()' was found in the specified scope }}
friend int Base::typedeffed_member(); // okay: should look through typedef
// These test that the friend is properly not being treated as a
// member function.
friend A operator|(const A& l, const A& r); // okay
friend A operator|(const A& r); // expected-error {{ overloaded 'operator|' must be a binary operator (has 1 parameter) }}
friend operator bool() const; // expected-error {{ must use a qualified name when declaring a conversion operator as a friend }}
typedef void ftypedef();
friend ftypedef typedeffed_function; // okay (because it's not declared as a member)
};
class PreDeclared;
int myoperation(float f) {
return (int) f;
}

View File

@ -0,0 +1,8 @@
// RUN: clang-cc -fsyntax-only -verify %s
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 class C; // okay
};

View File

@ -0,0 +1,10 @@
// RUN: clang-cc -fsyntax-only -verify %s
class A {
friend static class B; // expected-error {{'static' is invalid in friend declarations}}
friend extern class C; // expected-error {{'extern' is invalid in friend declarations}}
friend auto class D; // expected-error {{'auto' is invalid in friend declarations}}
friend register class E; // expected-error {{'register' is invalid in friend declarations}}
friend mutable class F; // expected-error {{'mutable' is invalid in friend declarations}}
friend typedef class G; // expected-error {{'typedef' is invalid in friend declarations}}
};

View File

@ -1,4 +1,4 @@
// RUN: clang-cc -fsyntax-only %s
// RUN: clang-cc -fsyntax-only -verify %s
class C {
friend class D;
@ -9,9 +9,24 @@ public:
void f();
};
friend int x; // expected-error {{'friend' used outside of class}}
friend class D {}; // expected-error {{'friend' used outside of class}}
union U {
int u1;
};
class B {
// 'A' here should refer to the declaration above.
friend class A;
void f(A *a) { a->f(); }
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 int; // expected-error {{friends can only be classes or functions}}
friend void myfunc();
void f(A *a) { a->f(); }
};