From: John McCall Date: Thu, 29 Apr 2010 23:50:39 +0000 (+0000) Subject: Rebuild the nested name specifiers in member-pointer declarator chunks when X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=63b4385822857374d035128dff3feac131465496;p=clang Rebuild the nested name specifiers in member-pointer declarator chunks when entering the current instantiation. Set up a little to preserve type location information for typename types while we're in there. Fixes a Boost failure. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@102673 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 02a86cd94d..4d107b5df2 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -3082,9 +3082,10 @@ public: const IdentifierInfo &II, SourceRange Range); - QualType RebuildTypeInCurrentInstantiation(QualType T, SourceLocation Loc, - DeclarationName Name); - void RebuildNestedNameSpecifierInCurrentInstantiation(CXXScopeSpec &SS); + TypeSourceInfo *RebuildTypeInCurrentInstantiation(TypeSourceInfo *T, + SourceLocation Loc, + DeclarationName Name); + bool RebuildNestedNameSpecifierInCurrentInstantiation(CXXScopeSpec &SS); std::string getTemplateArgumentBindingsText(const TemplateParameterList *Params, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index d125d414e6..7a70c32877 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1922,6 +1922,71 @@ static bool isNearlyMatchingFunction(ASTContext &Context, return true; } +/// NeedsRebuildingInCurrentInstantiation - Checks whether the given +/// declarator needs to be rebuilt in the current instantiation. +/// Any bits of declarator which appear before the name are valid for +/// consideration here. That's specifically the type in the decl spec +/// and the base type in any member-pointer chunks. +static bool RebuildDeclaratorInCurrentInstantiation(Sema &S, Declarator &D, + DeclarationName Name) { + // The types we specifically need to rebuild are: + // - typenames, typeofs, and decltypes + // - types which will become injected class names + // Of course, we also need to rebuild any type referencing such a + // type. It's safest to just say "dependent", but we call out a + // few cases here. + + DeclSpec &DS = D.getMutableDeclSpec(); + switch (DS.getTypeSpecType()) { + case DeclSpec::TST_typename: + case DeclSpec::TST_typeofType: + case DeclSpec::TST_typeofExpr: + case DeclSpec::TST_decltype: { + // Grab the type from the parser. + TypeSourceInfo *TSI = 0; + QualType T = S.GetTypeFromParser(DS.getTypeRep(), &TSI); + if (T.isNull() || !T->isDependentType()) break; + + // Make sure there's a type source info. This isn't really much + // of a waste; most dependent types should have type source info + // attached already. + if (!TSI) + TSI = S.Context.getTrivialTypeSourceInfo(T, DS.getTypeSpecTypeLoc()); + + // Rebuild the type in the current instantiation. + TSI = S.RebuildTypeInCurrentInstantiation(TSI, D.getIdentifierLoc(), Name); + if (!TSI) return true; + + // Store the new type back in the decl spec. + QualType LocType = S.CreateLocInfoType(TSI->getType(), TSI); + DS.UpdateTypeRep(LocType.getAsOpaquePtr()); + break; + } + + default: + // Nothing to do for these decl specs. + break; + } + + // It doesn't matter what order we do this in. + for (unsigned I = 0, E = D.getNumTypeObjects(); I != E; ++I) { + DeclaratorChunk &Chunk = D.getTypeObject(I); + + // The only type information in the declarator which can come + // before the declaration name is the base type of a member + // pointer. + if (Chunk.Kind != DeclaratorChunk::MemberPointer) + continue; + + // Rebuild the scope specifier in-place. + CXXScopeSpec &SS = Chunk.Mem.Scope(); + if (S.RebuildNestedNameSpecifierInCurrentInstantiation(SS)) + return true; + } + + return false; +} + Sema::DeclPtrTy Sema::HandleDeclarator(Scope *S, Declarator &D, MultiTemplateParamsArg TemplateParamLists, @@ -1944,35 +2009,47 @@ Sema::HandleDeclarator(Scope *S, Declarator &D, (S->getFlags() & Scope::TemplateParamScope) != 0) S = S->getParent(); - // If this is an out-of-line definition of a member of a class template - // or class template partial specialization, we may need to rebuild the - // type specifier in the declarator. See RebuildTypeInCurrentInstantiation() - // for more information. - // FIXME: cope with decltype(expr) and typeof(expr) once the rebuilder can - // handle expressions properly. - DeclSpec &DS = const_cast(D.getDeclSpec()); - if (D.getCXXScopeSpec().isSet() && !D.getCXXScopeSpec().isInvalid() && - isDependentScopeSpecifier(D.getCXXScopeSpec()) && - (DS.getTypeSpecType() == DeclSpec::TST_typename || - DS.getTypeSpecType() == DeclSpec::TST_typeofType || - DS.getTypeSpecType() == DeclSpec::TST_typeofExpr || - DS.getTypeSpecType() == DeclSpec::TST_decltype)) { - if (DeclContext *DC = computeDeclContext(D.getCXXScopeSpec(), true)) { - // FIXME: Preserve type source info. - QualType T = GetTypeFromParser(DS.getTypeRep()); - - DeclContext *SavedContext = CurContext; - CurContext = DC; - T = RebuildTypeInCurrentInstantiation(T, D.getIdentifierLoc(), Name); - CurContext = SavedContext; - - if (T.isNull()) - return DeclPtrTy(); - DS.UpdateTypeRep(T.getAsOpaquePtr()); + DeclContext *DC = CurContext; + if (D.getCXXScopeSpec().isInvalid()) + D.setInvalidType(); + else if (D.getCXXScopeSpec().isSet()) { + bool EnteringContext = !D.getDeclSpec().isFriendSpecified(); + DC = computeDeclContext(D.getCXXScopeSpec(), EnteringContext); + if (!DC) { + // If we could not compute the declaration context, it's because the + // declaration context is dependent but does not refer to a class, + // class template, or class template partial specialization. Complain + // and return early, to avoid the coming semantic disaster. + Diag(D.getIdentifierLoc(), + diag::err_template_qualified_declarator_no_match) + << (NestedNameSpecifier*)D.getCXXScopeSpec().getScopeRep() + << D.getCXXScopeSpec().getRange(); + return DeclPtrTy(); + } + + bool IsDependentContext = DC->isDependentContext(); + + if (!IsDependentContext && + RequireCompleteDeclContext(D.getCXXScopeSpec())) + return DeclPtrTy(); + + if (isa(DC) && !cast(DC)->hasDefinition()) { + Diag(D.getIdentifierLoc(), + diag::err_member_def_undefined_record) + << Name << DC << D.getCXXScopeSpec().getRange(); + D.setInvalidType(); + } + + // Check whether we need to rebuild the type of the given + // declaration in the current instantiation. + if (EnteringContext && IsDependentContext && + TemplateParamLists.size() != 0) { + ContextRAII SavedContext(*this, DC); + if (RebuildDeclaratorInCurrentInstantiation(*this, D, Name)) + D.setInvalidType(); } } - DeclContext *DC; NamedDecl *New; TypeSourceInfo *TInfo = 0; @@ -1982,10 +2059,7 @@ Sema::HandleDeclarator(Scope *S, Declarator &D, ForRedeclaration); // See if this is a redefinition of a variable in the same scope. - if (D.getCXXScopeSpec().isInvalid()) { - DC = CurContext; - D.setInvalidType(); - } else if (!D.getCXXScopeSpec().isSet()) { + if (!D.getCXXScopeSpec().isSet()) { bool IsLinkageLookup = false; // If the declaration we're planning to build will be a function @@ -2006,34 +2080,8 @@ Sema::HandleDeclarator(Scope *S, Declarator &D, if (IsLinkageLookup) Previous.clear(LookupRedeclarationWithLinkage); - DC = CurContext; LookupName(Previous, S, /* CreateBuiltins = */ IsLinkageLookup); } else { // Something like "int foo::x;" - DC = computeDeclContext(D.getCXXScopeSpec(), true); - - if (!DC) { - // If we could not compute the declaration context, it's because the - // declaration context is dependent but does not refer to a class, - // class template, or class template partial specialization. Complain - // and return early, to avoid the coming semantic disaster. - Diag(D.getIdentifierLoc(), - diag::err_template_qualified_declarator_no_match) - << (NestedNameSpecifier*)D.getCXXScopeSpec().getScopeRep() - << D.getCXXScopeSpec().getRange(); - return DeclPtrTy(); - } - - if (!DC->isDependentContext() && - RequireCompleteDeclContext(D.getCXXScopeSpec())) - return DeclPtrTy(); - - if (isa(DC) && !cast(DC)->hasDefinition()) { - Diag(D.getIdentifierLoc(), - diag::err_member_def_undefined_record) - << Name << DC << D.getCXXScopeSpec().getRange(); - D.setInvalidType(); - } - LookupQualifiedName(Previous, DC); // Don't consider using declarations as previous declarations for diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index ad6cc2f4c8..70a92ca2e7 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -5165,6 +5165,20 @@ Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK, return Context.getDependentNameType(Keyword, NNS, Name).getAsOpaquePtr(); } +static void FillTypeLoc(DependentNameTypeLoc TL, + SourceLocation TypenameLoc, + SourceRange QualifierRange) { + // FIXME: typename, qualifier range + TL.setNameLoc(TypenameLoc); +} + +static void FillTypeLoc(QualifiedNameTypeLoc TL, + SourceLocation TypenameLoc, + SourceRange QualifierRange) { + // FIXME: typename, qualifier range + TL.setNameLoc(TypenameLoc); +} + Sema::TypeResult Sema::ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, const IdentifierInfo &II, SourceLocation IdLoc) { @@ -5177,7 +5191,19 @@ Sema::ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, SourceRange(TypenameLoc, IdLoc)); if (T.isNull()) return true; - return T.getAsOpaquePtr(); + + TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T); + if (isa(T)) { + DependentNameTypeLoc TL = cast(TSI->getTypeLoc()); + // FIXME: fill inner type loc + FillTypeLoc(TL, TypenameLoc, SS.getRange()); + } else { + QualifiedNameTypeLoc TL = cast(TSI->getTypeLoc()); + // FIXME: fill inner type loc + FillTypeLoc(TL, TypenameLoc, SS.getRange()); + } + + return CreateLocInfoType(T, TSI).getAsOpaquePtr(); } Sema::TypeResult @@ -5196,11 +5222,21 @@ Sema::ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, // track of the nested-name-specifier. // FIXME: Note that the QualifiedNameType had the "typename" keyword! - return Context.getQualifiedNameType(NNS, T).getAsOpaquePtr(); + + T = Context.getQualifiedNameType(NNS, T); + TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T); + QualifiedNameTypeLoc TL = cast(TSI->getTypeLoc()); + // FIXME: fill inner type loc + FillTypeLoc(TL, TypenameLoc, SS.getRange()); + return CreateLocInfoType(T, TSI).getAsOpaquePtr(); } - return Context.getDependentNameType(ETK_Typename, NNS, TemplateId) - .getAsOpaquePtr(); + T = Context.getDependentNameType(ETK_Typename, NNS, TemplateId); + TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T); + DependentNameTypeLoc TL = cast(TSI->getTypeLoc()); + // FIXME: fill inner type loc + FillTypeLoc(TL, TypenameLoc, SS.getRange()); + return CreateLocInfoType(T, TSI).getAsOpaquePtr(); } /// \brief Build the type that describes a C++ typename specifier, @@ -5419,24 +5455,28 @@ CurrentInstantiationRebuilder::TransformDependentNameType(TypeLocBuilder &TLB, /// in X and returning a QualifiedNameType whose canonical type is the same /// as the canonical type of T*, allowing the return types of the out-of-line /// definition and the declaration to match. -QualType Sema::RebuildTypeInCurrentInstantiation(QualType T, SourceLocation Loc, - DeclarationName Name) { - if (T.isNull() || !T->isDependentType()) +TypeSourceInfo *Sema::RebuildTypeInCurrentInstantiation(TypeSourceInfo *T, + SourceLocation Loc, + DeclarationName Name) { + if (!T || !T->getType()->isDependentType()) return T; CurrentInstantiationRebuilder Rebuilder(*this, Loc, Name); return Rebuilder.TransformType(T); } -void Sema::RebuildNestedNameSpecifierInCurrentInstantiation(CXXScopeSpec &SS) { - if (SS.isInvalid()) return; +bool Sema::RebuildNestedNameSpecifierInCurrentInstantiation(CXXScopeSpec &SS) { + if (SS.isInvalid()) return true; NestedNameSpecifier *NNS = static_cast(SS.getScopeRep()); CurrentInstantiationRebuilder Rebuilder(*this, SS.getRange().getBegin(), DeclarationName()); NestedNameSpecifier *Rebuilt = Rebuilder.TransformNestedNameSpecifier(NNS, SS.getRange()); - if (Rebuilt) SS.setScopeRep(Rebuilt); + if (!Rebuilt) return true; + + SS.setScopeRep(Rebuilt); + return false; } /// \brief Produces a formatted string that describes the binding of diff --git a/test/SemaTemplate/nested-name-spec-template.cpp b/test/SemaTemplate/nested-name-spec-template.cpp index 1691db74a1..9d25a051e8 100644 --- a/test/SemaTemplate/nested-name-spec-template.cpp +++ b/test/SemaTemplate/nested-name-spec-template.cpp @@ -51,3 +51,16 @@ struct TestA { typedef typename N::template B::type type; // expected-error{{'B' following the 'template' keyword does not refer to a template}} \ // expected-error{{expected member name}} }; + +// Reduced from a Boost failure. +namespace test1 { + template struct pair { + T x; + T y; + + static T pair::* const mem_array[2]; + }; + + template + T pair::* const pair::mem_array[2] = { &pair::x, &pair::y }; +} diff --git a/test/SemaTemplate/typename-specifier-4.cpp b/test/SemaTemplate/typename-specifier-4.cpp index 280a1b4c39..8dfb60d707 100644 --- a/test/SemaTemplate/typename-specifier-4.cpp +++ b/test/SemaTemplate/typename-specifier-4.cpp @@ -113,6 +113,6 @@ namespace PR6463 { // FIXME: Improve source location info here. template typename A::type& A::a() { // expected-error{{found in multiple base classes}} - return x; // expected-error{{undeclared identifier}} + return x; } }