| //===- TemplateArgumentHasher.cpp - Hash Template Arguments -----*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "TemplateArgumentHasher.h" |
| #include "clang/AST/APValue.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/DeclTemplate.h" |
| #include "clang/AST/DeclarationName.h" |
| #include "clang/AST/TypeVisitor.h" |
| #include "clang/Basic/IdentifierTable.h" |
| #include "llvm/ADT/FoldingSet.h" |
| |
| using namespace clang; |
| |
| namespace { |
| |
| class TemplateArgumentHasher { |
| // If we bail out during the process of calculating hash values for |
| // template arguments for any reason. We're allowed to do it since |
| // TemplateArgumentHasher are only required to give the same hash value |
| // for the same template arguments, but not required to give different |
| // hash value for different template arguments. |
| // |
| // So in the worst case, it is still a valid implementation to give all |
| // inputs the same BailedOutValue as output. |
| bool BailedOut = false; |
| static constexpr unsigned BailedOutValue = 0x12345678; |
| |
| llvm::FoldingSetNodeID ID; |
| |
| public: |
| TemplateArgumentHasher() = default; |
| |
| void AddTemplateArgument(TemplateArgument TA); |
| |
| void AddInteger(unsigned V) { ID.AddInteger(V); } |
| |
| unsigned getValue() { |
| if (BailedOut) |
| return BailedOutValue; |
| |
| return ID.computeStableHash(); |
| } |
| |
| void setBailedOut() { BailedOut = true; } |
| |
| void AddType(const Type *T); |
| void AddQualType(QualType T); |
| void AddDecl(const Decl *D); |
| void AddStructuralValue(const APValue &); |
| void AddTemplateName(TemplateName Name); |
| void AddDeclarationName(DeclarationName Name); |
| void AddIdentifierInfo(const IdentifierInfo *II); |
| }; |
| |
| void TemplateArgumentHasher::AddTemplateArgument(TemplateArgument TA) { |
| const auto Kind = TA.getKind(); |
| AddInteger(Kind); |
| |
| switch (Kind) { |
| case TemplateArgument::Null: |
| llvm_unreachable("Expected valid TemplateArgument"); |
| case TemplateArgument::Type: |
| AddQualType(TA.getAsType()); |
| break; |
| case TemplateArgument::Declaration: |
| AddDecl(TA.getAsDecl()); |
| break; |
| case TemplateArgument::NullPtr: |
| ID.AddPointer(nullptr); |
| break; |
| case TemplateArgument::Integral: { |
| // There are integrals (e.g.: _BitInt(128)) that cannot be represented as |
| // any builtin integral type, so we use the hash of APSInt instead. |
| TA.getAsIntegral().Profile(ID); |
| break; |
| } |
| case TemplateArgument::StructuralValue: |
| AddQualType(TA.getStructuralValueType()); |
| AddStructuralValue(TA.getAsStructuralValue()); |
| break; |
| case TemplateArgument::Template: |
| case TemplateArgument::TemplateExpansion: |
| AddTemplateName(TA.getAsTemplateOrTemplatePattern()); |
| break; |
| case TemplateArgument::Expression: |
| // If we meet expression in template argument, it implies |
| // that the template is still dependent. It is meaningless |
| // to get a stable hash for the template. Bail out simply. |
| BailedOut = true; |
| break; |
| case TemplateArgument::Pack: |
| AddInteger(TA.pack_size()); |
| for (auto SubTA : TA.pack_elements()) { |
| AddTemplateArgument(SubTA); |
| } |
| break; |
| } |
| } |
| |
| void TemplateArgumentHasher::AddStructuralValue(const APValue &Value) { |
| auto Kind = Value.getKind(); |
| AddInteger(Kind); |
| |
| // 'APValue::Profile' uses pointer values to make hash for LValue and |
| // MemberPointer, but they differ from one compiler invocation to another. |
| // It may be difficult to handle such cases. Bail out simply. |
| |
| if (Kind == APValue::LValue || Kind == APValue::MemberPointer) { |
| BailedOut = true; |
| return; |
| } |
| |
| Value.Profile(ID); |
| } |
| |
| void TemplateArgumentHasher::AddTemplateName(TemplateName Name) { |
| switch (Name.getKind()) { |
| case TemplateName::Template: |
| AddDecl(Name.getAsTemplateDecl()); |
| break; |
| case TemplateName::QualifiedTemplate: { |
| QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName(); |
| AddTemplateName(QTN->getUnderlyingTemplate()); |
| break; |
| } |
| case TemplateName::OverloadedTemplate: |
| case TemplateName::AssumedTemplate: |
| case TemplateName::DependentTemplate: |
| case TemplateName::SubstTemplateTemplateParm: |
| case TemplateName::SubstTemplateTemplateParmPack: |
| BailedOut = true; |
| break; |
| case TemplateName::UsingTemplate: { |
| UsingShadowDecl *USD = Name.getAsUsingShadowDecl(); |
| if (USD) |
| AddDecl(USD->getTargetDecl()); |
| else |
| BailedOut = true; |
| break; |
| } |
| case TemplateName::DeducedTemplate: |
| AddTemplateName(Name.getAsDeducedTemplateName()->getUnderlying()); |
| break; |
| } |
| } |
| |
| void TemplateArgumentHasher::AddIdentifierInfo(const IdentifierInfo *II) { |
| assert(II && "Expecting non-null pointer."); |
| ID.AddString(II->getName()); |
| } |
| |
| void TemplateArgumentHasher::AddDeclarationName(DeclarationName Name) { |
| if (Name.isEmpty()) |
| return; |
| |
| switch (Name.getNameKind()) { |
| case DeclarationName::Identifier: |
| AddIdentifierInfo(Name.getAsIdentifierInfo()); |
| break; |
| case DeclarationName::ObjCZeroArgSelector: |
| case DeclarationName::ObjCOneArgSelector: |
| case DeclarationName::ObjCMultiArgSelector: |
| BailedOut = true; |
| break; |
| case DeclarationName::CXXConstructorName: |
| case DeclarationName::CXXDestructorName: |
| AddQualType(Name.getCXXNameType()); |
| break; |
| case DeclarationName::CXXOperatorName: |
| AddInteger(Name.getCXXOverloadedOperator()); |
| break; |
| case DeclarationName::CXXLiteralOperatorName: |
| AddIdentifierInfo(Name.getCXXLiteralIdentifier()); |
| break; |
| case DeclarationName::CXXConversionFunctionName: |
| AddQualType(Name.getCXXNameType()); |
| break; |
| case DeclarationName::CXXUsingDirective: |
| break; |
| case DeclarationName::CXXDeductionGuideName: { |
| if (auto *Template = Name.getCXXDeductionGuideTemplate()) |
| AddDecl(Template); |
| } |
| } |
| } |
| |
| void TemplateArgumentHasher::AddDecl(const Decl *D) { |
| const NamedDecl *ND = dyn_cast<NamedDecl>(D); |
| if (!ND) { |
| BailedOut = true; |
| return; |
| } |
| |
| AddDeclarationName(ND->getDeclName()); |
| } |
| |
| void TemplateArgumentHasher::AddQualType(QualType T) { |
| if (T.isNull()) { |
| BailedOut = true; |
| return; |
| } |
| SplitQualType split = T.split(); |
| AddInteger(split.Quals.getAsOpaqueValue()); |
| AddType(split.Ty); |
| } |
| |
| // Process a Type pointer. Add* methods call back into TemplateArgumentHasher |
| // while Visit* methods process the relevant parts of the Type. |
| // Any unhandled type will make the hash computation bail out. |
| class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { |
| typedef TypeVisitor<TypeVisitorHelper> Inherited; |
| llvm::FoldingSetNodeID &ID; |
| TemplateArgumentHasher &Hash; |
| |
| public: |
| TypeVisitorHelper(llvm::FoldingSetNodeID &ID, TemplateArgumentHasher &Hash) |
| : ID(ID), Hash(Hash) {} |
| |
| void AddDecl(const Decl *D) { |
| if (D) |
| Hash.AddDecl(D); |
| else |
| Hash.AddInteger(0); |
| } |
| |
| void AddQualType(QualType T) { Hash.AddQualType(T); } |
| |
| void AddType(const Type *T) { |
| if (T) |
| Hash.AddType(T); |
| else |
| Hash.AddInteger(0); |
| } |
| |
| void VisitQualifiers(Qualifiers Quals) { |
| Hash.AddInteger(Quals.getAsOpaqueValue()); |
| } |
| |
| void Visit(const Type *T) { Inherited::Visit(T); } |
| |
| // Unhandled types. Bail out simply. |
| void VisitType(const Type *T) { Hash.setBailedOut(); } |
| |
| void VisitAdjustedType(const AdjustedType *T) { |
| AddQualType(T->getOriginalType()); |
| } |
| |
| void VisitDecayedType(const DecayedType *T) { |
| // getDecayedType and getPointeeType are derived from getAdjustedType |
| // and don't need to be separately processed. |
| VisitAdjustedType(T); |
| } |
| |
| void VisitArrayType(const ArrayType *T) { |
| AddQualType(T->getElementType()); |
| Hash.AddInteger(llvm::to_underlying(T->getSizeModifier())); |
| VisitQualifiers(T->getIndexTypeQualifiers()); |
| } |
| void VisitConstantArrayType(const ConstantArrayType *T) { |
| T->getSize().Profile(ID); |
| VisitArrayType(T); |
| } |
| |
| void VisitAttributedType(const AttributedType *T) { |
| Hash.AddInteger(T->getAttrKind()); |
| AddQualType(T->getModifiedType()); |
| } |
| |
| void VisitBuiltinType(const BuiltinType *T) { Hash.AddInteger(T->getKind()); } |
| |
| void VisitComplexType(const ComplexType *T) { |
| AddQualType(T->getElementType()); |
| } |
| |
| void VisitDecltypeType(const DecltypeType *T) { |
| AddQualType(T->getUnderlyingType()); |
| } |
| |
| void VisitDeducedType(const DeducedType *T) { |
| AddQualType(T->getDeducedType()); |
| } |
| |
| void VisitAutoType(const AutoType *T) { VisitDeducedType(T); } |
| |
| void VisitDeducedTemplateSpecializationType( |
| const DeducedTemplateSpecializationType *T) { |
| Hash.AddTemplateName(T->getTemplateName()); |
| VisitDeducedType(T); |
| } |
| |
| void VisitFunctionType(const FunctionType *T) { |
| AddQualType(T->getReturnType()); |
| T->getExtInfo().Profile(ID); |
| Hash.AddInteger(T->isConst()); |
| Hash.AddInteger(T->isVolatile()); |
| Hash.AddInteger(T->isRestrict()); |
| } |
| |
| void VisitFunctionNoProtoType(const FunctionNoProtoType *T) { |
| VisitFunctionType(T); |
| } |
| |
| void VisitFunctionProtoType(const FunctionProtoType *T) { |
| Hash.AddInteger(T->getNumParams()); |
| for (auto ParamType : T->getParamTypes()) |
| AddQualType(ParamType); |
| |
| VisitFunctionType(T); |
| } |
| |
| void VisitMemberPointerType(const MemberPointerType *T) { |
| AddQualType(T->getPointeeType()); |
| AddType(T->getQualifier()->getAsType()); |
| if (auto *RD = T->getMostRecentCXXRecordDecl()) |
| AddDecl(RD->getCanonicalDecl()); |
| } |
| |
| void VisitPackExpansionType(const PackExpansionType *T) { |
| AddQualType(T->getPattern()); |
| } |
| |
| void VisitParenType(const ParenType *T) { AddQualType(T->getInnerType()); } |
| |
| void VisitPointerType(const PointerType *T) { |
| AddQualType(T->getPointeeType()); |
| } |
| |
| void VisitReferenceType(const ReferenceType *T) { |
| AddQualType(T->getPointeeTypeAsWritten()); |
| } |
| |
| void VisitLValueReferenceType(const LValueReferenceType *T) { |
| VisitReferenceType(T); |
| } |
| |
| void VisitRValueReferenceType(const RValueReferenceType *T) { |
| VisitReferenceType(T); |
| } |
| |
| void |
| VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) { |
| AddDecl(T->getAssociatedDecl()); |
| Hash.AddTemplateArgument(T->getArgumentPack()); |
| } |
| |
| void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) { |
| AddDecl(T->getAssociatedDecl()); |
| AddQualType(T->getReplacementType()); |
| } |
| |
| void VisitTagType(const TagType *T) { AddDecl(T->getDecl()); } |
| |
| void VisitRecordType(const RecordType *T) { VisitTagType(T); } |
| void VisitEnumType(const EnumType *T) { VisitTagType(T); } |
| |
| void VisitTemplateSpecializationType(const TemplateSpecializationType *T) { |
| Hash.AddInteger(T->template_arguments().size()); |
| for (const auto &TA : T->template_arguments()) { |
| Hash.AddTemplateArgument(TA); |
| } |
| Hash.AddTemplateName(T->getTemplateName()); |
| } |
| |
| void VisitTemplateTypeParmType(const TemplateTypeParmType *T) { |
| Hash.AddInteger(T->getDepth()); |
| Hash.AddInteger(T->getIndex()); |
| Hash.AddInteger(T->isParameterPack()); |
| } |
| |
| void VisitTypedefType(const TypedefType *T) { AddDecl(T->getDecl()); } |
| |
| void VisitElaboratedType(const ElaboratedType *T) { |
| AddQualType(T->getNamedType()); |
| } |
| |
| void VisitUnaryTransformType(const UnaryTransformType *T) { |
| AddQualType(T->getUnderlyingType()); |
| AddQualType(T->getBaseType()); |
| } |
| |
| void VisitVectorType(const VectorType *T) { |
| AddQualType(T->getElementType()); |
| Hash.AddInteger(T->getNumElements()); |
| Hash.AddInteger(llvm::to_underlying(T->getVectorKind())); |
| } |
| |
| void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); } |
| }; |
| |
| void TemplateArgumentHasher::AddType(const Type *T) { |
| assert(T && "Expecting non-null pointer."); |
| TypeVisitorHelper(ID, *this).Visit(T); |
| } |
| |
| } // namespace |
| |
| unsigned clang::serialization::StableHashForTemplateArguments( |
| llvm::ArrayRef<TemplateArgument> Args) { |
| TemplateArgumentHasher Hasher; |
| Hasher.AddInteger(Args.size()); |
| for (TemplateArgument Arg : Args) |
| Hasher.AddTemplateArgument(Arg); |
| return Hasher.getValue(); |
| } |