Path: blob/main/contrib/llvm-project/clang/lib/Serialization/TemplateArgumentHasher.cpp
213766 views
//===- TemplateArgumentHasher.cpp - Hash Template Arguments -----*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "TemplateArgumentHasher.h"9#include "clang/AST/APValue.h"10#include "clang/AST/Decl.h"11#include "clang/AST/DeclCXX.h"12#include "clang/AST/DeclTemplate.h"13#include "clang/AST/DeclarationName.h"14#include "clang/AST/TypeVisitor.h"15#include "clang/Basic/IdentifierTable.h"16#include "llvm/ADT/FoldingSet.h"17#include "llvm/Support/TimeProfiler.h"1819using namespace clang;2021namespace {2223class TemplateArgumentHasher {24// If we bail out during the process of calculating hash values for25// template arguments for any reason. We're allowed to do it since26// TemplateArgumentHasher are only required to give the same hash value27// for the same template arguments, but not required to give different28// hash value for different template arguments.29//30// So in the worst case, it is still a valid implementation to give all31// inputs the same BailedOutValue as output.32bool BailedOut = false;33static constexpr unsigned BailedOutValue = 0x12345678;3435llvm::FoldingSetNodeID ID;3637public:38TemplateArgumentHasher() = default;3940void AddTemplateArgument(TemplateArgument TA);4142void AddInteger(unsigned V) { ID.AddInteger(V); }4344unsigned getValue() {45if (BailedOut)46return BailedOutValue;4748return ID.computeStableHash();49}5051void setBailedOut() { BailedOut = true; }5253void AddType(const Type *T);54void AddQualType(QualType T);55void AddDecl(const Decl *D);56void AddStructuralValue(const APValue &);57void AddTemplateName(TemplateName Name);58void AddDeclarationName(DeclarationName Name);59void AddIdentifierInfo(const IdentifierInfo *II);60};6162void TemplateArgumentHasher::AddTemplateArgument(TemplateArgument TA) {63const auto Kind = TA.getKind();64AddInteger(Kind);6566switch (Kind) {67case TemplateArgument::Null:68// These can occur in incomplete substitutions performed with code69// completion (see PartialOverloading).70break;71case TemplateArgument::Type:72AddQualType(TA.getAsType());73break;74case TemplateArgument::Declaration:75AddDecl(TA.getAsDecl());76break;77case TemplateArgument::NullPtr:78ID.AddPointer(nullptr);79break;80case TemplateArgument::Integral: {81// There are integrals (e.g.: _BitInt(128)) that cannot be represented as82// any builtin integral type, so we use the hash of APSInt instead.83TA.getAsIntegral().Profile(ID);84break;85}86case TemplateArgument::StructuralValue:87AddQualType(TA.getStructuralValueType());88AddStructuralValue(TA.getAsStructuralValue());89break;90case TemplateArgument::Template:91case TemplateArgument::TemplateExpansion:92AddTemplateName(TA.getAsTemplateOrTemplatePattern());93break;94case TemplateArgument::Expression:95// If we meet expression in template argument, it implies96// that the template is still dependent. It is meaningless97// to get a stable hash for the template. Bail out simply.98BailedOut = true;99break;100case TemplateArgument::Pack:101AddInteger(TA.pack_size());102for (auto SubTA : TA.pack_elements()) {103AddTemplateArgument(SubTA);104}105break;106}107}108109void TemplateArgumentHasher::AddStructuralValue(const APValue &Value) {110auto Kind = Value.getKind();111AddInteger(Kind);112113// 'APValue::Profile' uses pointer values to make hash for LValue and114// MemberPointer, but they differ from one compiler invocation to another.115// It may be difficult to handle such cases. Bail out simply.116117if (Kind == APValue::LValue || Kind == APValue::MemberPointer) {118BailedOut = true;119return;120}121122Value.Profile(ID);123}124125void TemplateArgumentHasher::AddTemplateName(TemplateName Name) {126switch (Name.getKind()) {127case TemplateName::Template:128AddDecl(Name.getAsTemplateDecl());129break;130case TemplateName::QualifiedTemplate: {131QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName();132AddTemplateName(QTN->getUnderlyingTemplate());133break;134}135case TemplateName::OverloadedTemplate:136case TemplateName::AssumedTemplate:137case TemplateName::DependentTemplate:138case TemplateName::SubstTemplateTemplateParm:139case TemplateName::SubstTemplateTemplateParmPack:140BailedOut = true;141break;142case TemplateName::UsingTemplate: {143UsingShadowDecl *USD = Name.getAsUsingShadowDecl();144if (USD)145AddDecl(USD->getTargetDecl());146else147BailedOut = true;148break;149}150case TemplateName::DeducedTemplate:151AddTemplateName(Name.getAsDeducedTemplateName()->getUnderlying());152break;153}154}155156void TemplateArgumentHasher::AddIdentifierInfo(const IdentifierInfo *II) {157assert(II && "Expecting non-null pointer.");158ID.AddString(II->getName());159}160161void TemplateArgumentHasher::AddDeclarationName(DeclarationName Name) {162if (Name.isEmpty())163return;164165switch (Name.getNameKind()) {166case DeclarationName::Identifier:167AddIdentifierInfo(Name.getAsIdentifierInfo());168break;169case DeclarationName::ObjCZeroArgSelector:170case DeclarationName::ObjCOneArgSelector:171case DeclarationName::ObjCMultiArgSelector:172BailedOut = true;173break;174case DeclarationName::CXXConstructorName:175case DeclarationName::CXXDestructorName:176AddQualType(Name.getCXXNameType());177break;178case DeclarationName::CXXOperatorName:179AddInteger(Name.getCXXOverloadedOperator());180break;181case DeclarationName::CXXLiteralOperatorName:182AddIdentifierInfo(Name.getCXXLiteralIdentifier());183break;184case DeclarationName::CXXConversionFunctionName:185AddQualType(Name.getCXXNameType());186break;187case DeclarationName::CXXUsingDirective:188break;189case DeclarationName::CXXDeductionGuideName: {190if (auto *Template = Name.getCXXDeductionGuideTemplate())191AddDecl(Template);192}193}194}195196void TemplateArgumentHasher::AddDecl(const Decl *D) {197const NamedDecl *ND = dyn_cast<NamedDecl>(D);198if (!ND) {199BailedOut = true;200return;201}202203AddDeclarationName(ND->getDeclName());204}205206void TemplateArgumentHasher::AddQualType(QualType T) {207if (T.isNull()) {208BailedOut = true;209return;210}211SplitQualType split = T.split();212AddInteger(split.Quals.getAsOpaqueValue());213AddType(split.Ty);214}215216// Process a Type pointer. Add* methods call back into TemplateArgumentHasher217// while Visit* methods process the relevant parts of the Type.218// Any unhandled type will make the hash computation bail out.219class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> {220typedef TypeVisitor<TypeVisitorHelper> Inherited;221llvm::FoldingSetNodeID &ID;222TemplateArgumentHasher &Hash;223224public:225TypeVisitorHelper(llvm::FoldingSetNodeID &ID, TemplateArgumentHasher &Hash)226: ID(ID), Hash(Hash) {}227228void AddDecl(const Decl *D) {229if (D)230Hash.AddDecl(D);231else232Hash.AddInteger(0);233}234235void AddQualType(QualType T) { Hash.AddQualType(T); }236237void AddType(const Type *T) {238if (T)239Hash.AddType(T);240else241Hash.AddInteger(0);242}243244void VisitQualifiers(Qualifiers Quals) {245Hash.AddInteger(Quals.getAsOpaqueValue());246}247248void Visit(const Type *T) { Inherited::Visit(T); }249250// Unhandled types. Bail out simply.251void VisitType(const Type *T) { Hash.setBailedOut(); }252253void VisitAdjustedType(const AdjustedType *T) {254AddQualType(T->getOriginalType());255}256257void VisitDecayedType(const DecayedType *T) {258// getDecayedType and getPointeeType are derived from getAdjustedType259// and don't need to be separately processed.260VisitAdjustedType(T);261}262263void VisitArrayType(const ArrayType *T) {264AddQualType(T->getElementType());265Hash.AddInteger(llvm::to_underlying(T->getSizeModifier()));266VisitQualifiers(T->getIndexTypeQualifiers());267}268void VisitConstantArrayType(const ConstantArrayType *T) {269T->getSize().Profile(ID);270VisitArrayType(T);271}272273void VisitAttributedType(const AttributedType *T) {274Hash.AddInteger(T->getAttrKind());275AddQualType(T->getModifiedType());276}277278void VisitBuiltinType(const BuiltinType *T) { Hash.AddInteger(T->getKind()); }279280void VisitComplexType(const ComplexType *T) {281AddQualType(T->getElementType());282}283284void VisitDecltypeType(const DecltypeType *T) {285AddQualType(T->getUnderlyingType());286}287288void VisitDeducedType(const DeducedType *T) {289AddQualType(T->getDeducedType());290}291292void VisitAutoType(const AutoType *T) { VisitDeducedType(T); }293294void VisitDeducedTemplateSpecializationType(295const DeducedTemplateSpecializationType *T) {296Hash.AddTemplateName(T->getTemplateName());297VisitDeducedType(T);298}299300void VisitFunctionType(const FunctionType *T) {301AddQualType(T->getReturnType());302T->getExtInfo().Profile(ID);303Hash.AddInteger(T->isConst());304Hash.AddInteger(T->isVolatile());305Hash.AddInteger(T->isRestrict());306}307308void VisitFunctionNoProtoType(const FunctionNoProtoType *T) {309VisitFunctionType(T);310}311312void VisitFunctionProtoType(const FunctionProtoType *T) {313Hash.AddInteger(T->getNumParams());314for (auto ParamType : T->getParamTypes())315AddQualType(ParamType);316317VisitFunctionType(T);318}319320void VisitMemberPointerType(const MemberPointerType *T) {321AddQualType(T->getPointeeType());322AddType(T->getQualifier()->getAsType());323if (auto *RD = T->getMostRecentCXXRecordDecl())324AddDecl(RD->getCanonicalDecl());325}326327void VisitPackExpansionType(const PackExpansionType *T) {328AddQualType(T->getPattern());329}330331void VisitParenType(const ParenType *T) { AddQualType(T->getInnerType()); }332333void VisitPointerType(const PointerType *T) {334AddQualType(T->getPointeeType());335}336337void VisitReferenceType(const ReferenceType *T) {338AddQualType(T->getPointeeTypeAsWritten());339}340341void VisitLValueReferenceType(const LValueReferenceType *T) {342VisitReferenceType(T);343}344345void VisitRValueReferenceType(const RValueReferenceType *T) {346VisitReferenceType(T);347}348349void350VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) {351AddDecl(T->getAssociatedDecl());352Hash.AddTemplateArgument(T->getArgumentPack());353}354355void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) {356AddDecl(T->getAssociatedDecl());357AddQualType(T->getReplacementType());358}359360void VisitTagType(const TagType *T) { AddDecl(T->getDecl()); }361362void VisitRecordType(const RecordType *T) { VisitTagType(T); }363void VisitEnumType(const EnumType *T) { VisitTagType(T); }364365void VisitTemplateSpecializationType(const TemplateSpecializationType *T) {366Hash.AddInteger(T->template_arguments().size());367for (const auto &TA : T->template_arguments()) {368Hash.AddTemplateArgument(TA);369}370Hash.AddTemplateName(T->getTemplateName());371}372373void VisitTemplateTypeParmType(const TemplateTypeParmType *T) {374Hash.AddInteger(T->getDepth());375Hash.AddInteger(T->getIndex());376Hash.AddInteger(T->isParameterPack());377}378379void VisitTypedefType(const TypedefType *T) { AddDecl(T->getDecl()); }380381void VisitElaboratedType(const ElaboratedType *T) {382AddQualType(T->getNamedType());383}384385void VisitUnaryTransformType(const UnaryTransformType *T) {386AddQualType(T->getUnderlyingType());387AddQualType(T->getBaseType());388}389390void VisitVectorType(const VectorType *T) {391AddQualType(T->getElementType());392Hash.AddInteger(T->getNumElements());393Hash.AddInteger(llvm::to_underlying(T->getVectorKind()));394}395396void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); }397};398399void TemplateArgumentHasher::AddType(const Type *T) {400assert(T && "Expecting non-null pointer.");401TypeVisitorHelper(ID, *this).Visit(T);402}403404} // namespace405406unsigned clang::serialization::StableHashForTemplateArguments(407llvm::ArrayRef<TemplateArgument> Args) {408llvm::TimeTraceScope TimeScope("Stable Hash for Template Arguments");409TemplateArgumentHasher Hasher;410Hasher.AddInteger(Args.size());411for (TemplateArgument Arg : Args)412Hasher.AddTemplateArgument(Arg);413return Hasher.getValue();414}415416417