blob: 3c7177b83ba52327e83800d9fd478807a91e7ace [file] [log] [blame]
//===- 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();
}