blob: 3d7184e2172b43a9a2b43f288022142f8518ee85 [file] [log] [blame]
Luca Versari99fddff2022-05-25 10:22:32 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#include "lifetime_analysis/pointer_compatibility.h"
6
Dmitri Gribenkoa087d232023-07-10 08:03:46 -07007#include <cassert>
8
Luca Versari99fddff2022-05-25 10:22:32 -07009#include "lifetime_annotations/pointee_type.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/DeclCXX.h"
Dmitri Gribenkoa087d232023-07-10 08:03:46 -070012#include "clang/AST/Type.h"
13#include "llvm/Support/ErrorHandling.h"
Luca Versari99fddff2022-05-25 10:22:32 -070014
15namespace clang {
16namespace tidy {
17namespace lifetimes {
18
19// Returns whether `type` is an unsigned integer type or an enum with an
20// underlying unsigned integer type.
21// Note that, unlike this function, Type::isUnsignedIntegerType() considers
22// `bool` to be an unsigned integer type.
23static bool IsUnsignedIntegerOrEnumType(clang::QualType type) {
24 type = type.getCanonicalType();
25 return type->isUnsignedIntegerType() && !type->isBooleanType();
26}
27
28bool PointeesCompatible(clang::QualType pointee_type,
29 clang::QualType object_type,
30 clang::ASTContext& ast_context) {
31 assert(!pointee_type.isNull());
32 assert(!object_type.isNull());
33
34 pointee_type = pointee_type.getCanonicalType();
35 object_type = object_type.getCanonicalType();
36
37 // `void *`, `char *`, `unsigned char *` and `std::byte *` are allowed to
38 // point at anything.
39 if (pointee_type->isVoidType() || pointee_type->isCharType() ||
40 pointee_type->isStdByteType()) {
41 return true;
42 }
43
44 // Anything is allowed to point at `void`. IOW, a function is allowed to cast
45 // a void pointer back to any other type of pointer.
46 if (object_type->isVoidType()) {
47 return true;
48 }
49
50 // Records.
51 if (pointee_type->isRecordType()) {
52 const clang::CXXRecordDecl* pointee_record_decl =
53 pointee_type->getAsCXXRecordDecl();
54 const clang::CXXRecordDecl* object_record_decl =
55 object_type->getAsCXXRecordDecl();
56 // We leave the case where the records are the same to the hasSimilarType()
57 // case below.
58 if (pointee_record_decl && object_record_decl &&
59 (object_record_decl->isDerivedFrom(pointee_record_decl) ||
60 pointee_record_decl->isDerivedFrom(object_record_decl))) {
61 return true;
62 }
63 }
64
65 // A signed integer pointer may point to the unsigned variant of the integer
66 // type and vice versa -- so arbitrarily canonicalize integer types to the
67 // signed version.
68 if (IsUnsignedIntegerOrEnumType(pointee_type)) {
69 pointee_type = ast_context.getCorrespondingSignedType(pointee_type);
70 }
71 if (IsUnsignedIntegerOrEnumType(object_type)) {
72 object_type = ast_context.getCorrespondingSignedType(object_type);
73 }
74
75 return ast_context.hasSimilarType(pointee_type, object_type);
76}
77
78bool MayPointTo(clang::QualType pointer_type, clang::QualType object_type,
79 clang::ASTContext& ast_context) {
80 assert(!pointer_type.isNull());
81 assert(!object_type.isNull());
82
83 pointer_type = pointer_type.getCanonicalType();
84 object_type = object_type.getCanonicalType();
85
86 clang::QualType pointee_type = PointeeType(pointer_type);
87
88 if (pointee_type.isNull()) {
89 llvm::report_fatal_error("pointee_type is null");
90 }
91
92 return PointeesCompatible(pointee_type, object_type, ast_context);
93}
94
95} // namespace lifetimes
96} // namespace tidy
97} // namespace clang