xref: /llvm-project/clang/lib/Serialization/TemplateArgumentHasher.cpp (revision 20e904950967c125abc1e91f57e5a373987ff016)
1 //===- TemplateArgumentHasher.cpp - Hash Template Arguments -----*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "TemplateArgumentHasher.h"
10 #include "clang/AST/APValue.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/AST/DeclCXX.h"
13 #include "clang/AST/DeclTemplate.h"
14 #include "clang/AST/DeclarationName.h"
15 #include "clang/AST/TypeVisitor.h"
16 #include "clang/Basic/IdentifierTable.h"
17 #include "llvm/ADT/FoldingSet.h"
18 
19 using namespace clang;
20 
21 namespace {
22 
23 class TemplateArgumentHasher {
24   // If we bail out during the process of calculating hash values for
25   // template arguments for any reason. We're allowed to do it since
26   // TemplateArgumentHasher are only required to give the same hash value
27   // for the same template arguments, but not required to give different
28   // hash value for different template arguments.
29   //
30   // So in the worst case, it is still a valid implementation to give all
31   // inputs the same BailedOutValue as output.
32   bool BailedOut = false;
33   static constexpr unsigned BailedOutValue = 0x12345678;
34 
35   llvm::FoldingSetNodeID ID;
36 
37 public:
38   TemplateArgumentHasher() = default;
39 
40   void AddTemplateArgument(TemplateArgument TA);
41 
42   void AddInteger(unsigned V) { ID.AddInteger(V); }
43 
44   unsigned getValue() {
45     if (BailedOut)
46       return BailedOutValue;
47 
48     return ID.computeStableHash();
49   }
50 
51   void setBailedOut() { BailedOut = true; }
52 
53   void AddType(const Type *T);
54   void AddQualType(QualType T);
55   void AddDecl(const Decl *D);
56   void AddStructuralValue(const APValue &);
57   void AddTemplateName(TemplateName Name);
58   void AddDeclarationName(DeclarationName Name);
59   void AddIdentifierInfo(const IdentifierInfo *II);
60 };
61 
62 void TemplateArgumentHasher::AddTemplateArgument(TemplateArgument TA) {
63   const auto Kind = TA.getKind();
64   AddInteger(Kind);
65 
66   switch (Kind) {
67   case TemplateArgument::Null:
68     llvm_unreachable("Expected valid TemplateArgument");
69   case TemplateArgument::Type:
70     AddQualType(TA.getAsType());
71     break;
72   case TemplateArgument::Declaration:
73     AddDecl(TA.getAsDecl());
74     break;
75   case TemplateArgument::NullPtr:
76     ID.AddPointer(nullptr);
77     break;
78   case TemplateArgument::Integral: {
79     // There are integrals (e.g.: _BitInt(128)) that cannot be represented as
80     // any builtin integral type, so we use the hash of APSInt instead.
81     TA.getAsIntegral().Profile(ID);
82     break;
83   }
84   case TemplateArgument::StructuralValue:
85     AddQualType(TA.getStructuralValueType());
86     AddStructuralValue(TA.getAsStructuralValue());
87     break;
88   case TemplateArgument::Template:
89   case TemplateArgument::TemplateExpansion:
90     AddTemplateName(TA.getAsTemplateOrTemplatePattern());
91     break;
92   case TemplateArgument::Expression:
93     // If we meet expression in template argument, it implies
94     // that the template is still dependent. It is meaningless
95     // to get a stable hash for the template. Bail out simply.
96     BailedOut = true;
97     break;
98   case TemplateArgument::Pack:
99     AddInteger(TA.pack_size());
100     for (auto SubTA : TA.pack_elements()) {
101       AddTemplateArgument(SubTA);
102     }
103     break;
104   }
105 }
106 
107 void TemplateArgumentHasher::AddStructuralValue(const APValue &Value) {
108   auto Kind = Value.getKind();
109   AddInteger(Kind);
110 
111   // 'APValue::Profile' uses pointer values to make hash for LValue and
112   // MemberPointer, but they differ from one compiler invocation to another.
113   // It may be difficult to handle such cases. Bail out simply.
114 
115   if (Kind == APValue::LValue || Kind == APValue::MemberPointer) {
116     BailedOut = true;
117     return;
118   }
119 
120   Value.Profile(ID);
121 }
122 
123 void TemplateArgumentHasher::AddTemplateName(TemplateName Name) {
124   switch (Name.getKind()) {
125   case TemplateName::Template:
126     AddDecl(Name.getAsTemplateDecl());
127     break;
128   case TemplateName::QualifiedTemplate: {
129     QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName();
130     AddTemplateName(QTN->getUnderlyingTemplate());
131     break;
132   }
133   case TemplateName::OverloadedTemplate:
134   case TemplateName::AssumedTemplate:
135   case TemplateName::DependentTemplate:
136   case TemplateName::SubstTemplateTemplateParm:
137   case TemplateName::SubstTemplateTemplateParmPack:
138     BailedOut = true;
139     break;
140   case TemplateName::UsingTemplate: {
141     UsingShadowDecl *USD = Name.getAsUsingShadowDecl();
142     if (USD)
143       AddDecl(USD->getTargetDecl());
144     else
145       BailedOut = true;
146     break;
147   }
148   case TemplateName::DeducedTemplate:
149     AddTemplateName(Name.getAsDeducedTemplateName()->getUnderlying());
150     break;
151   }
152 }
153 
154 void TemplateArgumentHasher::AddIdentifierInfo(const IdentifierInfo *II) {
155   assert(II && "Expecting non-null pointer.");
156   ID.AddString(II->getName());
157 }
158 
159 void TemplateArgumentHasher::AddDeclarationName(DeclarationName Name) {
160   if (Name.isEmpty())
161     return;
162 
163   switch (Name.getNameKind()) {
164   case DeclarationName::Identifier:
165     AddIdentifierInfo(Name.getAsIdentifierInfo());
166     break;
167   case DeclarationName::ObjCZeroArgSelector:
168   case DeclarationName::ObjCOneArgSelector:
169   case DeclarationName::ObjCMultiArgSelector:
170     BailedOut = true;
171     break;
172   case DeclarationName::CXXConstructorName:
173   case DeclarationName::CXXDestructorName:
174     AddQualType(Name.getCXXNameType());
175     break;
176   case DeclarationName::CXXOperatorName:
177     AddInteger(Name.getCXXOverloadedOperator());
178     break;
179   case DeclarationName::CXXLiteralOperatorName:
180     AddIdentifierInfo(Name.getCXXLiteralIdentifier());
181     break;
182   case DeclarationName::CXXConversionFunctionName:
183     AddQualType(Name.getCXXNameType());
184     break;
185   case DeclarationName::CXXUsingDirective:
186     break;
187   case DeclarationName::CXXDeductionGuideName: {
188     if (auto *Template = Name.getCXXDeductionGuideTemplate())
189       AddDecl(Template);
190   }
191   }
192 }
193 
194 void TemplateArgumentHasher::AddDecl(const Decl *D) {
195   const NamedDecl *ND = dyn_cast<NamedDecl>(D);
196   if (!ND) {
197     BailedOut = true;
198     return;
199   }
200 
201   AddDeclarationName(ND->getDeclName());
202 }
203 
204 void TemplateArgumentHasher::AddQualType(QualType T) {
205   if (T.isNull()) {
206     BailedOut = true;
207     return;
208   }
209   SplitQualType split = T.split();
210   AddInteger(split.Quals.getAsOpaqueValue());
211   AddType(split.Ty);
212 }
213 
214 // Process a Type pointer.  Add* methods call back into TemplateArgumentHasher
215 // while Visit* methods process the relevant parts of the Type.
216 // Any unhandled type will make the hash computation bail out.
217 class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> {
218   typedef TypeVisitor<TypeVisitorHelper> Inherited;
219   llvm::FoldingSetNodeID &ID;
220   TemplateArgumentHasher &Hash;
221 
222 public:
223   TypeVisitorHelper(llvm::FoldingSetNodeID &ID, TemplateArgumentHasher &Hash)
224       : ID(ID), Hash(Hash) {}
225 
226   void AddDecl(const Decl *D) {
227     if (D)
228       Hash.AddDecl(D);
229     else
230       Hash.AddInteger(0);
231   }
232 
233   void AddQualType(QualType T) { Hash.AddQualType(T); }
234 
235   void AddType(const Type *T) {
236     if (T)
237       Hash.AddType(T);
238     else
239       Hash.AddInteger(0);
240   }
241 
242   void VisitQualifiers(Qualifiers Quals) {
243     Hash.AddInteger(Quals.getAsOpaqueValue());
244   }
245 
246   void Visit(const Type *T) { Inherited::Visit(T); }
247 
248   // Unhandled types. Bail out simply.
249   void VisitType(const Type *T) { Hash.setBailedOut(); }
250 
251   void VisitAdjustedType(const AdjustedType *T) {
252     AddQualType(T->getOriginalType());
253   }
254 
255   void VisitDecayedType(const DecayedType *T) {
256     // getDecayedType and getPointeeType are derived from getAdjustedType
257     // and don't need to be separately processed.
258     VisitAdjustedType(T);
259   }
260 
261   void VisitArrayType(const ArrayType *T) {
262     AddQualType(T->getElementType());
263     Hash.AddInteger(llvm::to_underlying(T->getSizeModifier()));
264     VisitQualifiers(T->getIndexTypeQualifiers());
265   }
266   void VisitConstantArrayType(const ConstantArrayType *T) {
267     T->getSize().Profile(ID);
268     VisitArrayType(T);
269   }
270 
271   void VisitAttributedType(const AttributedType *T) {
272     Hash.AddInteger(T->getAttrKind());
273     AddQualType(T->getModifiedType());
274   }
275 
276   void VisitBuiltinType(const BuiltinType *T) { Hash.AddInteger(T->getKind()); }
277 
278   void VisitComplexType(const ComplexType *T) {
279     AddQualType(T->getElementType());
280   }
281 
282   void VisitDecltypeType(const DecltypeType *T) {
283     AddQualType(T->getUnderlyingType());
284   }
285 
286   void VisitDeducedType(const DeducedType *T) {
287     AddQualType(T->getDeducedType());
288   }
289 
290   void VisitAutoType(const AutoType *T) { VisitDeducedType(T); }
291 
292   void VisitDeducedTemplateSpecializationType(
293       const DeducedTemplateSpecializationType *T) {
294     Hash.AddTemplateName(T->getTemplateName());
295     VisitDeducedType(T);
296   }
297 
298   void VisitFunctionType(const FunctionType *T) {
299     AddQualType(T->getReturnType());
300     T->getExtInfo().Profile(ID);
301     Hash.AddInteger(T->isConst());
302     Hash.AddInteger(T->isVolatile());
303     Hash.AddInteger(T->isRestrict());
304   }
305 
306   void VisitFunctionNoProtoType(const FunctionNoProtoType *T) {
307     VisitFunctionType(T);
308   }
309 
310   void VisitFunctionProtoType(const FunctionProtoType *T) {
311     Hash.AddInteger(T->getNumParams());
312     for (auto ParamType : T->getParamTypes())
313       AddQualType(ParamType);
314 
315     VisitFunctionType(T);
316   }
317 
318   void VisitMemberPointerType(const MemberPointerType *T) {
319     AddQualType(T->getPointeeType());
320     AddType(T->getClass());
321   }
322 
323   void VisitPackExpansionType(const PackExpansionType *T) {
324     AddQualType(T->getPattern());
325   }
326 
327   void VisitParenType(const ParenType *T) { AddQualType(T->getInnerType()); }
328 
329   void VisitPointerType(const PointerType *T) {
330     AddQualType(T->getPointeeType());
331   }
332 
333   void VisitReferenceType(const ReferenceType *T) {
334     AddQualType(T->getPointeeTypeAsWritten());
335   }
336 
337   void VisitLValueReferenceType(const LValueReferenceType *T) {
338     VisitReferenceType(T);
339   }
340 
341   void VisitRValueReferenceType(const RValueReferenceType *T) {
342     VisitReferenceType(T);
343   }
344 
345   void
346   VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) {
347     AddDecl(T->getAssociatedDecl());
348     Hash.AddTemplateArgument(T->getArgumentPack());
349   }
350 
351   void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) {
352     AddDecl(T->getAssociatedDecl());
353     AddQualType(T->getReplacementType());
354   }
355 
356   void VisitTagType(const TagType *T) { AddDecl(T->getDecl()); }
357 
358   void VisitRecordType(const RecordType *T) { VisitTagType(T); }
359   void VisitEnumType(const EnumType *T) { VisitTagType(T); }
360 
361   void VisitTemplateSpecializationType(const TemplateSpecializationType *T) {
362     Hash.AddInteger(T->template_arguments().size());
363     for (const auto &TA : T->template_arguments()) {
364       Hash.AddTemplateArgument(TA);
365     }
366     Hash.AddTemplateName(T->getTemplateName());
367   }
368 
369   void VisitTemplateTypeParmType(const TemplateTypeParmType *T) {
370     Hash.AddInteger(T->getDepth());
371     Hash.AddInteger(T->getIndex());
372     Hash.AddInteger(T->isParameterPack());
373   }
374 
375   void VisitTypedefType(const TypedefType *T) { AddDecl(T->getDecl()); }
376 
377   void VisitElaboratedType(const ElaboratedType *T) {
378     AddQualType(T->getNamedType());
379   }
380 
381   void VisitUnaryTransformType(const UnaryTransformType *T) {
382     AddQualType(T->getUnderlyingType());
383     AddQualType(T->getBaseType());
384   }
385 
386   void VisitVectorType(const VectorType *T) {
387     AddQualType(T->getElementType());
388     Hash.AddInteger(T->getNumElements());
389     Hash.AddInteger(llvm::to_underlying(T->getVectorKind()));
390   }
391 
392   void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); }
393 };
394 
395 void TemplateArgumentHasher::AddType(const Type *T) {
396   assert(T && "Expecting non-null pointer.");
397   TypeVisitorHelper(ID, *this).Visit(T);
398 }
399 
400 } // namespace
401 
402 unsigned clang::serialization::StableHashForTemplateArguments(
403     llvm::ArrayRef<TemplateArgument> Args) {
404   TemplateArgumentHasher Hasher;
405   Hasher.AddInteger(Args.size());
406   for (TemplateArgument Arg : Args)
407     Hasher.AddTemplateArgument(Arg);
408   return Hasher.getValue();
409 }
410