//===- 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(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 { typedef TypeVisitor 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->getClass()); } 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 Args) { TemplateArgumentHasher Hasher; Hasher.AddInteger(Args.size()); for (TemplateArgument Arg : Args) Hasher.AddTemplateArgument(Arg); return Hasher.getValue(); }