[cfe-commits] r100753 - in /cfe/trunk: include/clang/AST/Decl.h include/clang/AST/DeclFriend.h include/clang/AST/DeclTemplate.h lib/AST/Decl.cpp lib/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaTemplate.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/CXX/temp/temp.decls/temp.friend/p1.cpp

Douglas Gregor dgregor at apple.com
Thu Apr 8 08:16:52 PDT 2010


On Apr 8, 2010, at 2:05 AM, John McCall wrote:

> Author: rjmccall
> Date: Thu Apr  8 04:05:18 2010
> New Revision: 100753
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=100753&view=rev
> Log:
> Implement dependent friend function template specializations.
> 
> 
> Modified:
>    cfe/trunk/include/clang/AST/Decl.h
>    cfe/trunk/include/clang/AST/DeclFriend.h
>    cfe/trunk/include/clang/AST/DeclTemplate.h
>    cfe/trunk/lib/AST/Decl.cpp
>    cfe/trunk/lib/Sema/Sema.h
>    cfe/trunk/lib/Sema/SemaDecl.cpp
>    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
>    cfe/trunk/lib/Sema/SemaTemplate.cpp
>    cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
>    cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp
> 
> Modified: cfe/trunk/include/clang/AST/Decl.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=100753&r1=100752&r2=100753&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/AST/Decl.h (original)
> +++ cfe/trunk/include/clang/AST/Decl.h Thu Apr  8 04:05:18 2010
> @@ -31,7 +31,9 @@
> class TemplateArgumentList;
> class MemberSpecializationInfo;
> class FunctionTemplateSpecializationInfo;
> +class DependentFunctionTemplateSpecializationInfo;
> class TypeLoc;
> +class UnresolvedSetImpl;
> 
> /// \brief A container of type source information.
> ///
> @@ -1037,9 +1039,10 @@
>   /// FunctionTemplateSpecializationInfo, which contains information about
>   /// the template being specialized and the template arguments involved in
>   /// that specialization.
> -  llvm::PointerUnion3<FunctionTemplateDecl *, 
> +  llvm::PointerUnion4<FunctionTemplateDecl *, 
>                       MemberSpecializationInfo *,
> -                      FunctionTemplateSpecializationInfo *>
> +                      FunctionTemplateSpecializationInfo *,
> +                      DependentFunctionTemplateSpecializationInfo *>
>     TemplateOrSpecialization;
> 
> protected:
> @@ -1365,6 +1368,18 @@
>                                          void *InsertPos,
>                     TemplateSpecializationKind TSK = TSK_ImplicitInstantiation);
> 
> +  /// \brief Specifies that this function declaration is actually a
> +  /// dependent function template specialization.
> +  void setDependentTemplateSpecialization(ASTContext &Context,
> +                             const UnresolvedSetImpl &Templates,
> +                      const TemplateArgumentListInfo &TemplateArgs);
> +
> +  DependentFunctionTemplateSpecializationInfo *
> +  getDependentSpecializationInfo() const {
> +    return TemplateOrSpecialization.
> +             dyn_cast<DependentFunctionTemplateSpecializationInfo*>();
> +  }
> +
>   /// \brief Determine what kind of template instantiation this function
>   /// represents.
>   TemplateSpecializationKind getTemplateSpecializationKind() const;
> 
> Modified: cfe/trunk/include/clang/AST/DeclFriend.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclFriend.h?rev=100753&r1=100752&r2=100753&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/AST/DeclFriend.h (original)
> +++ cfe/trunk/include/clang/AST/DeclFriend.h Thu Apr  8 04:05:18 2010
> @@ -48,10 +48,6 @@
>   // Location of the 'friend' specifier.
>   SourceLocation FriendLoc;
> 
> -  // FIXME: Hack to keep track of whether this was a friend function
> -  // template specialization.
> -  bool WasSpecialization;

Yay!

> +/// \brief Provides information about a dependent function-template
> +/// specialization declaration.  Since explicit function template
> +/// specialization and instantiation declarations can only appear in
> +/// namespace scope, and you can only specialize a member of a
> +/// fully-specialized class, the only way to get one of these is in
> +/// a friend declaration like the following:
> +///
> +///   template <class T> void foo(T);
> +///   template <class T> class A {
> +///     friend void foo<>(T);
> +///   };
> +class DependentFunctionTemplateSpecializationInfo {
> +  union {
> +    // Force sizeof to be a multiple of sizeof(void*) so that the
> +    // trailing data is aligned.
> +    void *Aligner; 
> +
> +    struct {
> +      /// The number of potential template candidates.
> +      unsigned NumTemplates;
> +
> +      /// The number of template arguments.
> +      unsigned NumArgs;      
> +    } d;
> +  };
> +
> +  /// The locations of the left and right angle brackets.
> +  SourceRange AngleLocs;
> +
> +  FunctionTemplateDecl * const *getTemplates() const {
> +    return reinterpret_cast<FunctionTemplateDecl*const*>(this+1);
> +  }
> +
> +  const TemplateArgumentLoc *getTemplateArgs() const {
> +    return reinterpret_cast<const TemplateArgumentLoc*>(
> +             getTemplates()[getNumTemplates()]);
> +  }
> +
> +public:
> +  DependentFunctionTemplateSpecializationInfo(
> +                                 const UnresolvedSetImpl &Templates,
> +                                 const TemplateArgumentListInfo &TemplateArgs);
> +
> +  /// \brief Returns the number of function templates that this might
> +  /// be a specialization of.
> +  unsigned getNumTemplates() const {
> +    return d.NumTemplates;
> +  }
> +
> +  /// \brief Returns the i'th template candidate.
> +  FunctionTemplateDecl *getTemplate(unsigned I) const {
> +    assert(I < getNumTemplates() && "template index out of range");
> +    return getTemplates()[I];
> +  }
> +
> +  /// \brief Returns the number of explicit template arguments that were given.
> +  unsigned getNumTemplateArgs() const {
> +    return d.NumArgs;
> +  }
> +
> +  /// \brief Returns the nth template argument.
> +  const TemplateArgumentLoc &getTemplateArg(unsigned I) const {
> +    assert(I < getNumTemplateArgs() && "template arg index out of range");
> +    return getTemplateArgs()[I];
> +  }
> +
> +  SourceLocation getLAngleLoc() const {
> +    return AngleLocs.getBegin();
> +  }
> +
> +  SourceLocation getRAngleLoc() const {
> +    return AngleLocs.getEnd();
> +  }
> +};

Very good, I like this representation. 

I do wonder if we'll need to hold on to a TemplateName, for the case where the name of the template we're specializing is dependent, e.g.,

template<typename T, typename U>
struct X {
  friend void T::foo<>(U);
};

(that's yet another level of complexity)

> Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=100753&r1=100752&r2=100753&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Thu Apr  8 04:05:18 2010
> @@ -5638,9 +5638,6 @@
>   FrD->setAccess(AS_public);
>   CurContext->addDecl(FrD);
> 
> -  if (D.getName().getKind() == UnqualifiedId::IK_TemplateId)
> -    FrD->setSpecialization(true);
> -
>   return DeclPtrTy::make(ND);
> }

--Hack :)

> 
> Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=100753&r1=100752&r2=100753&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaTemplate.cpp Thu Apr  8 04:05:18 2010
> @@ -4083,6 +4083,42 @@
>   return false;
> }
> 
> +/// \brief Perform semantic analysis for the given dependent function
> +/// template specialization.  The only possible way to get a dependent
> +/// function template specialization is with a friend declaration,
> +/// like so:
> +///
> +///   template <class T> void foo(T);
> +///   template <class T> class A {
> +///     friend void foo<>(T);
> +///   };
> +///
> +/// There really isn't any useful analysis we can do here, so we
> +/// just store the information.
> +bool
> +Sema::CheckDependentFunctionTemplateSpecialization(FunctionDecl *FD,
> +                   const TemplateArgumentListInfo &ExplicitTemplateArgs,
> +                                                   LookupResult &Previous) {
> +  // Remove anything from Previous that isn't a function template in
> +  // the correct context.
> +  DeclContext *FDLookupContext = FD->getDeclContext()->getLookupContext();
> +  LookupResult::Filter F = Previous.makeFilter();
> +  while (F.hasNext()) {
> +    NamedDecl *D = F.next()->getUnderlyingDecl();
> +    if (!isa<FunctionTemplateDecl>(D) ||
> +        !FDLookupContext->Equals(D->getDeclContext()->getLookupContext()))
> +      F.erase();
> +  }
> +  F.done();
> +
> +  // Should this be diagnosed here?
> +  if (Previous.empty()) return true;

It should be diagnosed somewhere, since the template we're specializing must already have been declared. That said, I'm having trouble finding the standard reference. C++0x [temp.expl.spec]p3 says as much for specializations, so I think that's the closest we'll get.

> Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=100753&r1=100752&r2=100753&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Thu Apr  8 04:05:18 2010
> @@ -497,18 +497,11 @@
>   NamedDecl *ND = D->getFriendDecl();
>   assert(ND && "friend decl must be a decl or a type!");
> 
> -  // FIXME: We have a problem here, because the nested call to Visit(ND)
> -  // will inject the thing that the friend references into the current
> -  // owner, which is wrong.
> -  Decl *NewND;
> -
> -  // Hack to make this work almost well pending a rewrite.
> -  if (D->wasSpecialization()) {
> -    // Totally egregious hack to work around PR5866
> -    return 0;
> -  } else {
> -    NewND = Visit(ND);
> -  }

Excellent. Is PR5866 now fixed?

> +  // All of the Visit implementations for the various potential friend
> +  // declarations have to be carefully written to work for friend
> +  // objects, with the most important detail being that the target
> +  // decl should almost certainly not be placed in Owner.
> +  Decl *NewND = Visit(ND);
>   if (!NewND) return 0;
> 
>   FriendDecl *FD =
> @@ -1024,11 +1017,47 @@
> 
>   bool Redeclaration = false;
>   bool OverloadableAttrRequired = false;
> +  bool isExplicitSpecialization = false;
> 
>   LookupResult Previous(SemaRef, Function->getDeclName(), SourceLocation(),
>                         Sema::LookupOrdinaryName, Sema::ForRedeclaration);
> 
> -  if (TemplateParams || !FunctionTemplate) {
> +  if (DependentFunctionTemplateSpecializationInfo *Info
> +        = D->getDependentSpecializationInfo()) {
> +    assert(isFriend && "non-friend has dependent specialization info?");
> +
> +    // This needs to be set now for future sanity.
> +    Function->setObjectOfFriendDecl(/*HasPrevious*/ true);
> +
> +    // Instantiate the explicit template arguments.
> +    TemplateArgumentListInfo ExplicitArgs(Info->getLAngleLoc(),
> +                                          Info->getRAngleLoc());
> +    for (unsigned I = 0, E = Info->getNumTemplateArgs(); I != E; ++I) {
> +      TemplateArgumentLoc Loc;
> +      if (SemaRef.Subst(Info->getTemplateArg(I), Loc, TemplateArgs))
> +        return 0;
> +
> +      ExplicitArgs.addArgument(Loc);
> +    }
> +
> +    // Map the candidate templates to their instantiations.
> +    for (unsigned I = 0, E = Info->getNumTemplates(); I != E; ++I) {
> +      Decl *Temp = SemaRef.FindInstantiatedDecl(D->getLocation(),
> +                                                Info->getTemplate(I),
> +                                                TemplateArgs);
> +      if (!Temp) return 0;

I guess the instantiation of template declarations could matter if we're friending a specialization of a member function template within the current instantiation. That'll be fun to test :)

	- Doug



More information about the cfe-commits mailing list