Index: include/clang/AST/TypeInstantiationMatcher.h =================================================================== --- /dev/null +++ include/clang/AST/TypeInstantiationMatcher.h @@ -0,0 +1,145 @@ +//===--- TypeInstantiationMatcher.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines TypeMatcher that is used to check if one type is or could be an +// instantiation of other type. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_TYPEINSTANTIATIONMATCHER_H +#define LLVM_CLANG_AST_TYPEINSTANTIATIONMATCHER_H + +#include "clang/AST/TypeMatcher.h" +#include "clang/AST/DeclTemplate.h" +#include + +namespace clang { + +/// \brief Visitor class used to check if one type is instantiation of another. +/// +class TypeInstantiationMatcher : public TypeMatcher { + std::map ParamMapping; + +public: + + bool VisitType(QualType PQ, QualType TQ) { + return PQ.getQualifiers() == TQ.getQualifiers(); + } + + bool VisitBuiltinType(QualType PQ, QualType TQ) { + return PQ.getCanonicalType() == TQ.getCanonicalType(); + } + + bool VisitConstantArrayType(QualType PQ, QualType TQ) { + const auto *P = cast(PQ.getTypePtr()); + const auto *T = cast(TQ.getTypePtr()); + return P->getSize() == T->getSize(); + } + + bool VisitVectorType(QualType PQ, QualType TQ) { + const auto *P = PQ->getAs(); + const auto *T = TQ->getAs(); + return P->getNumElements() == T->getNumElements(); + } + + bool VisitFunctionProtoType(QualType PQ, QualType TQ) { + const auto *P = PQ->getAs(); + const auto *T = TQ->getAs(); + if (P->getNumParams() != T->getNumParams()) + return false; + if (P->exceptions().size() != T->exceptions().size()) + return false; + return true; + } + + bool VisitTemplateTypeParmType(QualType PQ, QualType TQ) { + auto P = PQ->getCanonicalTypeInternal()->getAs(); + auto Ptr = ParamMapping.find(P); + if (Ptr != ParamMapping.end()) + return Ptr->second == TQ; + ParamMapping[P] = TQ; + return true; + } + + bool TraverseTemplateTypeParmType(QualType PQ, QualType TQ) { + return WalkUpFromTemplateTypeParmType(PQ, TQ); + } + + bool matchTemplateParametersAndArguments(const TemplateParameterList &PL, + const TemplateArgumentList &AL) { + if (PL.size() != AL.size()) + return false; + for (unsigned I = 0, E = PL.size(); I < E; ++I) { + const NamedDecl *PD = PL.getParam(I); + const TemplateArgument &TA = AL.get(I); + TemplateArgument::ArgKind kind = TA.getKind(); + if (const auto *TTP = dyn_cast(PD)) { + if (kind != TemplateArgument::Type) + return false; + auto Type = TTP->getTypeForDecl()->getCanonicalTypeInternal() + ->getAs(); + auto Ptr = ParamMapping.find(Type); + if (Ptr != ParamMapping.end()) + return Ptr->second == TA.getAsType(); + ParamMapping[Type] = TA.getAsType(); + } + } + return true; + } + + bool TraverseTemplateSpecializationType(QualType PQ, QualType TQ) { + return WalkUpFromTemplateSpecializationType(PQ, TQ); + } + + bool VisitTemplateSpecializationType(QualType PQ, QualType TQ) { + const auto *P = PQ->getAs(); + TemplateName PTN = P->getTemplateName(); + if (PTN.getKind() != TemplateName::Template) + return false; + TemplateDecl *PT = PTN.getAsTemplateDecl(); + auto *PTD = dyn_cast(PT); + if (!PTD) + return false; + auto PParams = PTD->getTemplateParameters(); + + if (const CXXRecordDecl *CD = TQ->getAsCXXRecordDecl()) { + if (const auto *SD = dyn_cast(CD)) + if (ClassTemplateDecl *TD = SD->getSpecializedTemplate()) { + if (PTD->getCanonicalDecl() != TD->getCanonicalDecl()) + return false; + const TemplateArgumentList &TArgs = SD->getTemplateArgs(); + if (PParams->size() != TArgs.size()) + return false; + return matchTemplateParametersAndArguments(*PParams, TArgs); + } + return false; + } + + return true; + } + + bool VisitInjectedClassNameType(QualType PQ, QualType TQ) { + const auto *P = PQ->getAs(); + return match(QualType(P->getInjectedSpecializationType()), + TQ.getUnqualifiedType()); + } + + bool TraverseInjectedClassNameType(QualType PQ, QualType TQ) { + return WalkUpFromInjectedClassNameType(PQ, TQ); + } + + bool VisitObjCObjectType(QualType PQ, QualType TQ) { + const auto *P = PQ->getAs(); + const auto *T = TQ->getAs(); + return P->getTypeArgsAsWritten().size() == T->getTypeArgsAsWritten().size(); + } +}; + +} +#endif Index: include/clang/AST/TypeMatcher.h =================================================================== --- /dev/null +++ include/clang/AST/TypeMatcher.h @@ -0,0 +1,428 @@ +//===--- TypeMatcher.h - Visitor for Type subclasses ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the TypeMatcher interface, which recursively compares +// two types. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_TYPEMATCHER_H +#define LLVM_CLANG_AST_TYPEMATCHER_H + +#include "clang/AST/Type.h" + +namespace clang { + +// A helper macro to implement short-circuiting when recursing. It invokes +// CALL_EXPR, which must be a method call, on the derived object. +#define TRY_TO(CALL_EXPR) \ + do { \ + if (!getDerived().CALL_EXPR) \ + return false; \ + } while (0) + + +/// \brief A visitor that traverses one type, a "pattern" and compares it with +/// another type which is traversed synchronously. +/// +/// The visitor is used much like other visitor classes, for instance, +/// RecursiveASTVisitor. The necessary functionality is implemented in a new +/// class that must be inherited from this template according to Curiously +/// Recurring Template Pattern: +/// +/// \code +/// class AMatcher : public TypeMatcher { +/// ... +/// }; +/// \endcode +/// +/// The derived class overrides methods declared in TypeMatcher to implement the +/// necessary behavior. +/// +/// Most of matching is made by miscellaneous Visit methods that are called for +/// pairs of QualTypes found in type hierarchy. There are Visit methods for each +/// subclass of Type, including abstract types. So, for a particular pattern +/// type usually there are several Visit methods that can be called. Other +/// methods of this visitor defines which Visit methods are called and in what +/// order. +/// +/// Pattern matching starts by calling method TraverseType, which implements +/// polymorphic operation on an a pair of objects of type derived from Type: +/// +/// \code +/// TraverseType(PatternType, EvaluatedType) +/// \endcode +/// +/// The function returns true if \c EvaluatedType matches \c PatternType, false +/// otherwise. +/// +/// Depending on actual type of \c PatternType this function dispatches call to +/// function that processes particular type, for instance +/// \c TraverseConstantArrayType, if the pattern type is \c ConstantArrayType. +/// This function first calls function \c WalkUpFromConstantArrayType, which +/// calls appropriate Visit methods. Then it recursively calls itself on +/// components of the pattern type. +/// +/// Function \c WalkUpFrom* exists for every type. It calls Visit methods for +/// the particular pattern type. Default implementation makes calls the starting +/// from the most general type, so if \c PatternType is ConstantArrayType, then +/// sequence of calls is as follows: +/// +/// \li VisitType +/// \li VisitArrayType +/// \li VisitConstantArrayType +/// +/// If any of \c Visit calls returns false, types do not match, any other checks +/// are not made. +/// +template +class TypeMatcher { +public: + + // Entry point of matcher. + bool match(QualType PatternT, QualType InstT) { + return TraverseType(PatternT.getCanonicalType(), InstT.getCanonicalType()); + } + + // Dispatches call to particular Traverse* method. + bool TraverseType(QualType PatternT, QualType InstT) { + switch (PatternT->getTypeClass()) { +#define ABSTRACT_TYPE(CLASS, BASE) +#define TYPE(CLASS, BASE) \ + case Type::CLASS: \ + return getDerived().Traverse##CLASS##Type(PatternT, InstT); +#include "clang/AST/TypeNodes.def" + default: + llvm_unreachable("Unknown type class!"); + } + return false; + } + + // Declare Traverse*() for all concrete Type classes. +#define ABSTRACT_TYPE(CLASS, BASE) +#define TYPE(CLASS, BASE) bool Traverse##CLASS##Type(QualType P, QualType T); +#include "clang/AST/TypeNodes.def" + + // Define WalkUpFrom*() for all Type classes. +#define TYPE(CLASS, BASE) \ + bool WalkUpFrom##CLASS##Type(QualType P, QualType S) { \ + TRY_TO(WalkUpFrom##BASE(P, S)); \ + TRY_TO(Visit##CLASS##Type(P, S)); \ + return true; \ + } +#include "clang/AST/TypeNodes.def" + + // Generic version of WalkUpFrom*, which is called first for every type + // matching. + bool WalkUpFromType(QualType PatternT, QualType T) { + return getDerived().VisitType(PatternT, T); + } + + // Define default Visit*() for all Type classes. +#define TYPE(CLASS, BASE) \ + bool Visit##CLASS##Type(QualType P, QualType T) { \ + return P->getTypeClass() == T->getTypeClass(); \ + } +#include "clang/AST/TypeNodes.def" + + // Generic version of Visit*, it is called first for every type matching. + bool VisitType(QualType PatternT, QualType T) { + return true; + } + + /// \brief Return a reference to the derived class. + ImplClass &getDerived() { return *static_cast(this); } + + bool TraverseTemplateName(const TemplateName &PN, const TemplateName &TN) { + return PN.getKind() == TN.getKind(); + } + + bool TraverseTemplateArguments(const TemplateArgument *PArgs, unsigned PNum, + const TemplateArgument *SArgs, unsigned SNum) { + if (PNum != SNum) + return false; + for (unsigned I = 0; I != PNum; ++I) { + TRY_TO(TraverseTemplateArgument(PArgs[I], SArgs[I])); + } + return true; + } + + bool TraverseTemplateArgument(const TemplateArgument &PArg, + const TemplateArgument &SArg) { + if (PArg.getKind() != SArg.getKind()) + return false; + switch (PArg.getKind()) { + case TemplateArgument::Null: + case TemplateArgument::Declaration: + case TemplateArgument::Integral: + case TemplateArgument::NullPtr: + return true; + + case TemplateArgument::Type: + return getDerived().TraverseType(PArg.getAsType(), SArg.getAsType()); + + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + return getDerived().TraverseTemplateName( + PArg.getAsTemplateOrTemplatePattern(), + SArg.getAsTemplateOrTemplatePattern()); + + case TemplateArgument::Expression: + return getDerived().TraverseStmt(PArg.getAsExpr(), SArg.getAsExpr()); + + case TemplateArgument::Pack: + return getDerived().TraverseTemplateArguments(PArg.pack_begin(), + PArg.pack_size(), SArg.pack_begin(), SArg.pack_size()); + } + + return true; + } + + bool TraverseNestedNameSpecifier(NestedNameSpecifier *PNNS, + NestedNameSpecifier *SNNS) { + if (!PNNS) + return !SNNS; + if (!SNNS) + return false; + + TRY_TO(TraverseNestedNameSpecifier(PNNS->getPrefix(), SNNS->getPrefix())); + + if (PNNS->getKind() != SNNS->getKind()) + return false; + switch (PNNS->getKind()) { + case NestedNameSpecifier::Identifier: + case NestedNameSpecifier::Namespace: + case NestedNameSpecifier::NamespaceAlias: + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Super: + return true; + + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + TRY_TO(TraverseType(QualType(PNNS->getAsType(), 0), + QualType(SNNS->getAsType(), 0))); + } + + return true; + } + + // This function in fact compares expressions, that may be found as a part of + // type definition (as in VariableArrayType for instance). + bool TraverseStmt(Stmt *P, Stmt *S) { + if (!P || !S) + return !P == !S; + Expr *PE = cast(P); + Expr *SE = cast(S); + return TraverseType(PE->getType(), SE->getType()); + } +}; + +// Implementation of Traverse* methods. +// +// These methods are obtained from corresponding definitions of +// RecursiveASTVisitor almost mechanically. The main difference is that +// Traverse* functions get arguments in pairs: for pattern type and for sample. + +// Defines method TypeMatcher::Traverse* for the given TYPE. +// This macro makes available variables P and T, that represent pattern type +// class and sample type class respectively. +#define DEF_TRAVERSE_TYPE(TYPE, CODE) \ + template \ + bool TypeMatcher::Traverse##TYPE(QualType PQ, QualType TQ) { \ + if (!WalkUpFrom##TYPE(PQ, TQ)) \ + return false; \ + const TYPE *P = cast(PQ.getTypePtr()); \ + const TYPE *T = cast(TQ.getTypePtr()); \ + (void)P; (void)T; \ + CODE; \ + return true; \ + } + +DEF_TRAVERSE_TYPE(BuiltinType, {}) + +DEF_TRAVERSE_TYPE(ComplexType, { + TRY_TO(TraverseType(P->getElementType(), T->getElementType())); +}) + +DEF_TRAVERSE_TYPE(PointerType, { + TRY_TO(TraverseType(P->getPointeeType(), T->getPointeeType())); +}) + +DEF_TRAVERSE_TYPE(BlockPointerType, { + TRY_TO(TraverseType(P->getPointeeType(), T->getPointeeType())); +}) + +DEF_TRAVERSE_TYPE(LValueReferenceType, { + TRY_TO(TraverseType(P->getPointeeType(), T->getPointeeType())); +}) + +DEF_TRAVERSE_TYPE(RValueReferenceType, { + TRY_TO(TraverseType(P->getPointeeType(), T->getPointeeType())); +}) + +DEF_TRAVERSE_TYPE(MemberPointerType, { + TRY_TO(TraverseType(QualType(P->getClass(), 0), QualType(T->getClass(), 0))); + TRY_TO(TraverseType(P->getPointeeType(), T->getPointeeType())); +}) + +DEF_TRAVERSE_TYPE(AdjustedType, { + TRY_TO(TraverseType(P->getOriginalType(), T->getOriginalType())); +}) + +DEF_TRAVERSE_TYPE(DecayedType, { + TRY_TO(TraverseType(P->getOriginalType(), T->getOriginalType())); +}) + +DEF_TRAVERSE_TYPE(ConstantArrayType, { + TRY_TO(TraverseType(P->getElementType(), T->getElementType())); +}) + +DEF_TRAVERSE_TYPE(IncompleteArrayType, { + TRY_TO(TraverseType(P->getElementType(), T->getElementType())); +}) + +DEF_TRAVERSE_TYPE(VariableArrayType, { + TRY_TO(TraverseType(P->getElementType(), T->getElementType())); + TRY_TO(TraverseStmt(P->getSizeExpr(), T->getSizeExpr())); +}) + +DEF_TRAVERSE_TYPE(DependentSizedArrayType, { + TRY_TO(TraverseType(P->getElementType(), T->getElementType())); + TRY_TO(TraverseStmt(P->getSizeExpr(), T->getSizeExpr())); +}) + +DEF_TRAVERSE_TYPE(DependentSizedExtVectorType, { + TRY_TO(TraverseStmt(P->getSizeExpr(), T->getSizeExpr())); + TRY_TO(TraverseType(P->getElementType(), T->getElementType())); +}) + +DEF_TRAVERSE_TYPE(VectorType, { + TRY_TO(TraverseType(P->getElementType(), T->getElementType())); +}) + +DEF_TRAVERSE_TYPE(ExtVectorType, { + TRY_TO(TraverseType(P->getElementType(), T->getElementType())); +}) + +DEF_TRAVERSE_TYPE(FunctionNoProtoType, { + TRY_TO(TraverseType(P->getReturnType(), T->getReturnType())); +}) + +DEF_TRAVERSE_TYPE(FunctionProtoType, { + // Number of parameters must be equal for pattern and sample types, otherwise + // we cannot compare these types in pairs. This check must be done by some of + // Visit* methods. The same is for number of exceptions. + assert(P->getNumParams() == T->getNumParams()); + assert(P->exceptions().size() == T->exceptions().size()); + + TRY_TO(TraverseType(P->getReturnType(), T->getReturnType())); + + for (unsigned I = 0; I < P->getNumParams(); ++I) { + TRY_TO(TraverseType(P->getParamType(I), T->getParamType(I))); + } + + for (unsigned I = 0; I < P->exceptions().size(); ++I) { + TRY_TO(TraverseType(P->exceptions()[I], T->exceptions()[I])); + } + + TRY_TO(TraverseStmt(P->getNoexceptExpr(), T->getNoexceptExpr())); +}) + +DEF_TRAVERSE_TYPE(UnresolvedUsingType, {}) +DEF_TRAVERSE_TYPE(TypedefType, {}) + +DEF_TRAVERSE_TYPE(TypeOfExprType, { + TRY_TO(TraverseStmt(P->getUnderlyingExpr(), T->getUnderlyingExpr())); +}) + +DEF_TRAVERSE_TYPE(TypeOfType, { + TRY_TO(TraverseType(P->getUnderlyingType(), T->getUnderlyingType())); +}) + +DEF_TRAVERSE_TYPE(DecltypeType, { + TRY_TO(TraverseStmt(P->getUnderlyingExpr(), T->getUnderlyingExpr())); +}) + +DEF_TRAVERSE_TYPE(UnaryTransformType, { + TRY_TO(TraverseType(P->getBaseType(), T->getBaseType())); + TRY_TO(TraverseType(P->getUnderlyingType(), T->getUnderlyingType())); +}) + +DEF_TRAVERSE_TYPE(AutoType, { + TRY_TO(TraverseType(P->getDeducedType(), T->getDeducedType())); +}) + +DEF_TRAVERSE_TYPE(RecordType, {}) +DEF_TRAVERSE_TYPE(EnumType, {}) +DEF_TRAVERSE_TYPE(TemplateTypeParmType, {}) +DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, {}) +DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, {}) + +DEF_TRAVERSE_TYPE(TemplateSpecializationType, { + TRY_TO(TraverseTemplateName(P->getTemplateName(), T->getTemplateName())); + TRY_TO(TraverseTemplateArguments(P->getArgs(), P->getNumArgs(), + T->getArgs(), T->getNumArgs())); +}) + +DEF_TRAVERSE_TYPE(InjectedClassNameType, {}) + +DEF_TRAVERSE_TYPE(AttributedType, { + TRY_TO(TraverseType(P->getModifiedType(), T->getModifiedType())); +}) + +DEF_TRAVERSE_TYPE(ParenType, { + TRY_TO(TraverseType(P->getInnerType(), T->getInnerType())); +}) + +DEF_TRAVERSE_TYPE(ElaboratedType, { + TRY_TO(TraverseNestedNameSpecifier(P->getQualifier(), T->getQualifier())); + TRY_TO(TraverseType(P->getNamedType(), T->getNamedType())); +}) + +DEF_TRAVERSE_TYPE(DependentNameType, { + TRY_TO(TraverseNestedNameSpecifier(P->getQualifier(), T->getQualifier())); +}) + +DEF_TRAVERSE_TYPE(DependentTemplateSpecializationType, { + TRY_TO(TraverseNestedNameSpecifier(P->getQualifier(), T->getQualifier())); + TRY_TO(TraverseTemplateArguments(P->getArgs(), P->getNumArgs(), + T->getArgs(), T->getNumArgs())); +}) + +DEF_TRAVERSE_TYPE(PackExpansionType, { + TRY_TO(TraverseType(P->getPattern(), T->getPattern())); +}) + +DEF_TRAVERSE_TYPE(ObjCInterfaceType, {}) + +DEF_TRAVERSE_TYPE(ObjCObjectType, { + assert(P->getTypeArgsAsWritten().size() == T->getTypeArgsAsWritten().size()); + return false; + TRY_TO(TraverseType(P->getBaseType(), T->getBaseType())); + for (unsigned I = 0, E = P->getTypeArgsAsWritten().size(); I != E; ++I) { + TRY_TO(TraverseType(P->getTypeArgsAsWritten()[I], + T->getTypeArgsAsWritten()[I])); + } +}) + +DEF_TRAVERSE_TYPE(ObjCObjectPointerType, { + TRY_TO(TraverseType(P->getPointeeType(), T->getPointeeType())); }) + +DEF_TRAVERSE_TYPE(AtomicType, { + TRY_TO(TraverseType(P->getValueType(), T->getValueType())); +}) + +DEF_TRAVERSE_TYPE(PipeType, { + TRY_TO(TraverseType(P->getElementType(), T->getElementType())); +}) + +#undef DEF_TRAVERSE_TYPE + +} +#endif Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -282,6 +282,7 @@ def InitializerOverrides : DiagGroup<"initializer-overrides">; def NonNull : DiagGroup<"nonnull">; def NonPODVarargs : DiagGroup<"non-pod-varargs">; +def NonTemplateFriend : DiagGroup<"non-template-friend">; def ClassVarargs : DiagGroup<"class-varargs", [NonPODVarargs]>; def : DiagGroup<"nonportable-cfstrings">; def NonVirtualDtor : DiagGroup<"non-virtual-dtor">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1150,6 +1150,13 @@ "enclosing namespace is a Microsoft extension; add a nested name specifier">, InGroup; def err_pure_friend : Error<"friend declaration cannot have a pure-specifier">; +def warn_non_template_friend : Warning<"friend declaration %q0 depends on " + "template parameter but is not a function template">, + InGroup; +def note_add_template_friend_decl : Note<"declare function outside class " + "template to suppress this warning">; +def note_befriend_template : Note<"to befriend a template specialization, " + "make sure the function template has already been declared and use '<>'">; def err_invalid_member_in_interface : Error< "%select{data member |non-public member function |static member function |" Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5752,7 +5752,8 @@ QualType CheckTemplateIdType(TemplateName Template, SourceLocation TemplateLoc, - TemplateArgumentListInfo &TemplateArgs); + TemplateArgumentListInfo &TemplateArgs, + bool IsFriend = false); TypeResult ActOnTemplateIdType(CXXScopeSpec &SS, SourceLocation TemplateKWLoc, @@ -9462,6 +9463,15 @@ /// attempts to add itself into the container void CheckObjCCircularContainer(ObjCMessageExpr *Message); + /// \brief Set of file level friend function declared in template classes. + /// Such functions are not added to redeclaration chains until instantiation + /// of proper templates, but they are needed for checks. + SmallVector FriendsOfTemplates; + + /// \brief Check dependent friend functions for misinterpretation as function + /// templates. + void CheckDependentFriends(); + void AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE); void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc, bool DeleteWasArrayForm); Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -689,6 +689,7 @@ LateTemplateParserCleanup(OpaqueParser); CheckDelayedMemberExceptionSpecs(); + CheckDependentFriends(); } // All delayed member exception specs should be checked or we end up accepting Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -24,6 +24,7 @@ #include "clang/AST/ExprOpenMP.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" +#include "clang/AST/TypeInstantiationMatcher.h" #include "clang/Analysis/Analyses/FormatString.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/TargetBuiltins.h" @@ -10922,3 +10923,123 @@ << ArgumentExpr->getSourceRange() << TypeTagExpr->getSourceRange(); } + +namespace { + +/// \brief Helper class used to check if a friend declaration may refer to +/// another function declaration. +/// +/// The class is used to compare two function declarations, one is a friend +/// function declared in template class, the other is a function declared at +/// file level. Both functions must have the same name, this class checks only +/// function types. +/* +class FunctionMatcher : public TypeMatcher { + const Type *InstType; +public: + FunctionMatcher() : InstType(nullptr) {} + + bool VisitTemplateSpecializationType(const TemplateSpecializationType *T) { + if (T == InstType) + return true; + TemplateName TN = T->getTemplateName(); + if (TN.getKind() != TemplateName::Template) + return false; + TemplateDecl *TemplD = TN.getAsTemplateDecl(); + auto *ClassTD = dyn_cast(TemplD); + if (!ClassTD) + return false; + auto Params = ClassTD->getTemplateParameters(); + + if (CXXRecordDecl *IClassD = InstType->getAsCXXRecordDecl()) { + auto *IClassSD = dyn_cast(IClassD); + if (!IClassSD) + return false; + ClassTemplateDecl *IClassTD = IClassSD->getSpecializedTemplate(); + if (ClassTD->getCanonicalDecl() != IClassTD->getCanonicalDecl()) + return false; + const TemplateArgumentList &IArgs = IClassSD->getTemplateArgs(); + if (Params->size() != IArgs.size()) + return false; + for (unsigned I = 0; I < Params->size(); ++I) { + TypeDecl *Param = cast(Params->getParam(I)); + const TemplateArgument &IArg = IArgs.get(I); + QualType IArgT = IArg.getAsType(); + if (!IArgT.isNull()) { + if (!match(Param->getTypeForDecl(), IArgT.getTypePtr())) + return false; + } + } + return true; + } + + if (auto *ITST = dyn_cast(InstType)) { + TemplateName ITN = ITST->getTemplateName(); + if (ITN.getKind() != TemplateName::Template) + return false; + TemplateDecl *ITemplD = ITN.getAsTemplateDecl(); + auto *IClassTD = dyn_cast(ITemplD); + if (!IClassTD) + return false; + return IClassTD->getCanonicalDecl() == ClassTD->getCanonicalDecl(); + } + return false; + } +}; +*/ + +/// \brief Given a friend function declaration checks if it might be misused. +static void CheckDependentFriend(Sema &S, FunctionDecl *FriendD) { + if (FriendD->isInvalidDecl()) + return; + LookupResult FRes(S, FriendD->getNameInfo(), Sema::LookupOrdinaryName); + if (S.LookupQualifiedName(FRes, FriendD->getDeclContext())) { + QualType FriendT = FriendD->getType().getCanonicalType(); + // First check if there is suitable function template, this is more probable + // misuse case. + for (NamedDecl *D : FRes) { + if (D->isInvalidDecl()) + continue; + if (auto *FTD = dyn_cast(D)) { + FunctionDecl *FD = FTD->getTemplatedDecl(); + QualType FDT = FD->getType().getCanonicalType(); + if (TypeInstantiationMatcher().match(FriendT, FDT)) { + // Appropriate function template is found. + S.Diag(FriendD->getLocation(), diag::warn_non_template_friend) + << FriendD; + SourceLocation NameLoc = FriendD->getNameInfo().getLocEnd(); + SourceLocation PastName = S.getLocForEndOfToken(NameLoc); + S.Diag(PastName, diag::note_befriend_template) + << FixItHint::CreateInsertion(PastName, "<>"); + return; + } + } + } + // Then check for suitable functions that uses particular specialization of + // parameter type. + for (NamedDecl *D : FRes) { + if (D->isInvalidDecl()) + continue; + if (auto *FD = dyn_cast(D)) { + QualType FT = FD->getType().getCanonicalType(); + if (TypeInstantiationMatcher().match(FriendT, FT)) + // This is suitable file-level function, do not issue warnings. + return; + } + } + } + S.Diag(FriendD->getLocation(), diag::warn_non_template_friend) << FriendD; + S.Diag(FriendD->getLocation(), diag::note_add_template_friend_decl); + S.Diag(FriendD->getLocation(), diag::note_befriend_template); +} + +} + +void Sema::CheckDependentFriends() { + for (FunctionDecl *FriendD : FriendsOfTemplates) { + if (FriendD->getType()->isDependentType() && + !FriendD->isThisDeclarationADefinition()) + CheckDependentFriend(*this, FriendD); + } + FriendsOfTemplates.clear(); +} Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -8615,6 +8615,15 @@ AddToScope = false; } + if (isFriend && !NewFD->isInvalidDecl()) { + if (TemplateParamLists.empty() && !DC->isRecord() && + !isFunctionTemplateSpecialization && + NewFD->getType()->isDependentType() && + !NewFD->isThisDeclarationADefinition()) { + FriendsOfTemplates.push_back(NewFD); + } + } + return NewFD; } Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/SemaInternal.h" +#include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTLambda.h" Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -2113,7 +2113,8 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, SourceLocation TemplateLoc, - TemplateArgumentListInfo &TemplateArgs) { + TemplateArgumentListInfo &TemplateArgs, + bool IsFriend) { DependentTemplateName *DTN = Name.getUnderlying().getAsDependentTemplateName(); if (DTN && DTN->isIdentifier()) @@ -2203,7 +2204,7 @@ // TODO: in theory this could be a simple hashtable lookup; most // changes to CurContext don't change the set of current // instantiations. - if (isa(Template)) { + if (isa(Template) && !IsFriend) { for (DeclContext *Ctx = CurContext; Ctx; Ctx = Ctx->getLookupParent()) { // If we get out to a namespace, we're done. if (Ctx->isFileContext()) break; Index: test/CXX/drs/dr3xx.cpp =================================================================== --- test/CXX/drs/dr3xx.cpp +++ test/CXX/drs/dr3xx.cpp @@ -291,7 +291,9 @@ template void g(N::A<0>::B<0>); namespace N { - template struct I { friend bool operator==(const I&, const I&); }; + template struct I { friend bool operator==(const I&, const I&); }; // expected-warning{{friend declaration 'dr321::N::operator==' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} } N::I i, j; bool x = i == j; Index: test/CXX/drs/dr5xx.cpp =================================================================== --- test/CXX/drs/dr5xx.cpp +++ test/CXX/drs/dr5xx.cpp @@ -581,8 +581,12 @@ namespace dr557 { // dr557: yes template struct S { - friend void f(S *); - friend void g(S > *); + friend void f(S *); // expected-warning{{friend declaration 'dr557::f' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} + friend void g(S > *); // expected-warning{{friend declaration 'dr557::g' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} }; void x(S *p, S > *q) { f(p); Index: test/SemaCXX/friend.cpp =================================================================== --- test/SemaCXX/friend.cpp +++ test/SemaCXX/friend.cpp @@ -379,3 +379,111 @@ X *q = p; } } + + +template void pr23342_func(T x); +template +struct pr23342_C1 { + friend void pr23342_func<>(T x); + friend bool func(T x); // expected-warning{{friend declaration 'func' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} + friend bool func2(int x); + template friend bool func3(T2 x); + friend T func4(); // expected-warning{{friend declaration 'func4' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; + +namespace pr23342 { + +template +struct C1 { + friend void pr23342_func<>(T x); + friend bool func(T x); // expected-warning{{friend declaration 'pr23342::func' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} + friend bool func2(int x); + template friend bool func3(T2 x); + friend T func4(); // expected-warning{{friend declaration 'pr23342::func4' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; + +template +struct Arg { + friend bool operator==(const Arg& lhs, T rhs) { + return false; + } + friend bool operator!=(const Arg& lhs, T rhs); // expected-warning{{friend declaration 'pr23342::operator!=' depends on template parameter but is not a function template}} + // expected-note@-1{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; +template +bool operator!=(const Arg& lhs, T rhs) { + return true; +} +bool foo() { + Arg arg; + return (arg == 42) || (arg != 42); +} + + +template class C0 { + friend void func0(C0 &); // expected-warning{{friend declaration 'pr23342::func0' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; + +template class C0a { + friend void func0a(C0a &); // expected-warning{{friend declaration 'pr23342::func0a' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; +void func0a(C0a x); +void func0a(C0a *x); +void func0a(const C0a &x); +int func0a(C0a &x); + +template class C0b { + friend void func0b(C0b &x); +}; +void func0b(C0b &x); + + +template class C1a { + friend void func1a(T x, C1a &y); // expected-warning{{friend declaration 'pr23342::func1a' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; +void func1a(char x, C1a &y); + +template class C1b { + friend void func1b(T x, C1b &y); +}; +void func1b(int x, C1b &y); + + +template class C2a { + friend void func2a(C2a &); // expected-warning{{friend declaration 'pr23342::func2a' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} +}; +template +void func2a(const C2a &x); + + +template class C2b { + friend void func2b(C2b &); // expected-warning{{friend declaration 'pr23342::func2b' depends on template parameter but is not a function template}} + // expected-note@-1{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}}; +}; +template +void func2b(C2b &x); + +template class C2c; +template +void func2c(C2c &x); +template class C2c { + friend void func2c<>(C2c &); +}; + +} Index: test/SemaCXX/overload-call.cpp =================================================================== --- test/SemaCXX/overload-call.cpp +++ test/SemaCXX/overload-call.cpp @@ -574,13 +574,17 @@ // Ensure that overload resolution attempts to complete argument types when // performing ADL. template struct S { - friend int f(const S&); + friend int f(const S&); // expected-warning{{friend declaration 'IncompleteArg::f' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} }; extern S s; int k = f(s); template struct Op { - friend bool operator==(const Op &, const Op &); + friend bool operator==(const Op &, const Op &); // expected-warning{{friend declaration 'IncompleteArg::operator==' depends on template parameter but is not a function template}} + // expected-note@-1{{declare function outside class template to suppress this warning}} + // expected-note@-2{{to befriend a template specialization, make sure the function template has already been declared and use '<>'}} }; extern Op op; bool b = op == op;