aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2009-09-26 06:47:28 +0000
committerDouglas Gregor <dgregor@apple.com>2009-09-26 06:47:28 +0000
commitd85bea2affdd59d83d1be7d24b97f436484c3625 (patch)
treec54ffce5ad16d80e08ccbb6ebfd047206c93ac1a /lib/Sema
parent74e3c92aa3eb1750ac2eb89994046eb69db39100 (diff)
Rework the Parse-Sema interaction for friends to better support friend
class templates. We now treat friend class templates much more like normal class templates, except that they still get special name lookup rules. Fixes PR5057 and eliminates a bunch of spurious diagnostics in <iostream>. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82848 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema')
-rw-r--r--lib/Sema/SemaDecl.cpp61
-rw-r--r--lib/Sema/SemaDeclCXX.cpp8
-rw-r--r--lib/Sema/SemaTemplate.cpp40
3 files changed, 81 insertions, 28 deletions
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) {