1 | //===-- RecordOps.cpp -------------------------------------------*- 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 | // Operations on records (structs, classes, and unions). |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/Analysis/FlowSensitive/RecordOps.h" |
14 | |
15 | #define DEBUG_TYPE "dataflow" |
16 | |
17 | namespace clang::dataflow { |
18 | |
19 | static void copyField(const ValueDecl &Field, StorageLocation *SrcFieldLoc, |
20 | StorageLocation *DstFieldLoc, RecordStorageLocation &Dst, |
21 | Environment &Env) { |
22 | assert(Field.getType()->isReferenceType() || |
23 | (SrcFieldLoc != nullptr && DstFieldLoc != nullptr)); |
24 | |
25 | if (Field.getType()->isRecordType()) { |
26 | copyRecord(Src&: cast<RecordStorageLocation>(Val&: *SrcFieldLoc), |
27 | Dst&: cast<RecordStorageLocation>(Val&: *DstFieldLoc), Env); |
28 | } else if (Field.getType()->isReferenceType()) { |
29 | Dst.setChild(D: Field, Loc: SrcFieldLoc); |
30 | } else { |
31 | if (Value *Val = Env.getValue(Loc: *SrcFieldLoc)) |
32 | Env.setValue(Loc: *DstFieldLoc, Val&: *Val); |
33 | else |
34 | Env.clearValue(Loc: *DstFieldLoc); |
35 | } |
36 | } |
37 | |
38 | static void copySyntheticField(QualType FieldType, StorageLocation &SrcFieldLoc, |
39 | StorageLocation &DstFieldLoc, Environment &Env) { |
40 | if (FieldType->isRecordType()) { |
41 | copyRecord(Src&: cast<RecordStorageLocation>(Val&: SrcFieldLoc), |
42 | Dst&: cast<RecordStorageLocation>(Val&: DstFieldLoc), Env); |
43 | } else { |
44 | if (Value *Val = Env.getValue(Loc: SrcFieldLoc)) |
45 | Env.setValue(Loc: DstFieldLoc, Val&: *Val); |
46 | else |
47 | Env.clearValue(Loc: DstFieldLoc); |
48 | } |
49 | } |
50 | |
51 | void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, |
52 | Environment &Env) { |
53 | auto SrcType = Src.getType().getCanonicalType().getUnqualifiedType(); |
54 | auto DstType = Dst.getType().getCanonicalType().getUnqualifiedType(); |
55 | |
56 | auto SrcDecl = SrcType->getAsCXXRecordDecl(); |
57 | auto DstDecl = DstType->getAsCXXRecordDecl(); |
58 | |
59 | [[maybe_unused]] bool compatibleTypes = |
60 | SrcType == DstType || |
61 | (SrcDecl != nullptr && DstDecl != nullptr && |
62 | (SrcDecl->isDerivedFrom(DstDecl) || DstDecl->isDerivedFrom(SrcDecl))); |
63 | |
64 | LLVM_DEBUG({ |
65 | if (!compatibleTypes) { |
66 | llvm::dbgs() << "Source type " << Src.getType() << "\n" ; |
67 | llvm::dbgs() << "Destination type " << Dst.getType() << "\n" ; |
68 | } |
69 | }); |
70 | assert(compatibleTypes); |
71 | |
72 | if (SrcType == DstType || (SrcDecl != nullptr && DstDecl != nullptr && |
73 | SrcDecl->isDerivedFrom(DstDecl))) { |
74 | for (auto [Field, DstFieldLoc] : Dst.children()) |
75 | copyField(Field: *Field, SrcFieldLoc: Src.getChild(D: *Field), DstFieldLoc, Dst, Env); |
76 | for (const auto &[Name, DstFieldLoc] : Dst.synthetic_fields()) |
77 | copySyntheticField(FieldType: DstFieldLoc->getType(), SrcFieldLoc&: Src.getSyntheticField(Name), |
78 | DstFieldLoc&: *DstFieldLoc, Env); |
79 | } else { |
80 | for (auto [Field, SrcFieldLoc] : Src.children()) |
81 | copyField(Field: *Field, SrcFieldLoc, DstFieldLoc: Dst.getChild(D: *Field), Dst, Env); |
82 | for (const auto &[Name, SrcFieldLoc] : Src.synthetic_fields()) |
83 | copySyntheticField(FieldType: SrcFieldLoc->getType(), SrcFieldLoc&: *SrcFieldLoc, |
84 | DstFieldLoc&: Dst.getSyntheticField(Name), Env); |
85 | } |
86 | } |
87 | |
88 | bool recordsEqual(const RecordStorageLocation &Loc1, const Environment &Env1, |
89 | const RecordStorageLocation &Loc2, const Environment &Env2) { |
90 | LLVM_DEBUG({ |
91 | if (Loc2.getType().getCanonicalType().getUnqualifiedType() != |
92 | Loc1.getType().getCanonicalType().getUnqualifiedType()) { |
93 | llvm::dbgs() << "Loc1 type " << Loc1.getType() << "\n" ; |
94 | llvm::dbgs() << "Loc2 type " << Loc2.getType() << "\n" ; |
95 | } |
96 | }); |
97 | assert(Loc2.getType().getCanonicalType().getUnqualifiedType() == |
98 | Loc1.getType().getCanonicalType().getUnqualifiedType()); |
99 | |
100 | for (auto [Field, FieldLoc1] : Loc1.children()) { |
101 | StorageLocation *FieldLoc2 = Loc2.getChild(D: *Field); |
102 | |
103 | assert(Field->getType()->isReferenceType() || |
104 | (FieldLoc1 != nullptr && FieldLoc2 != nullptr)); |
105 | |
106 | if (Field->getType()->isRecordType()) { |
107 | if (!recordsEqual(Loc1: cast<RecordStorageLocation>(Val&: *FieldLoc1), Env1, |
108 | Loc2: cast<RecordStorageLocation>(Val&: *FieldLoc2), Env2)) |
109 | return false; |
110 | } else if (Field->getType()->isReferenceType()) { |
111 | if (FieldLoc1 != FieldLoc2) |
112 | return false; |
113 | } else if (Env1.getValue(Loc: *FieldLoc1) != Env2.getValue(Loc: *FieldLoc2)) { |
114 | return false; |
115 | } |
116 | } |
117 | |
118 | for (const auto &[Name, SynthFieldLoc1] : Loc1.synthetic_fields()) { |
119 | if (SynthFieldLoc1->getType()->isRecordType()) { |
120 | if (!recordsEqual( |
121 | Loc1: *cast<RecordStorageLocation>(Val: SynthFieldLoc1), Env1, |
122 | Loc2: cast<RecordStorageLocation>(Val&: Loc2.getSyntheticField(Name)), Env2)) |
123 | return false; |
124 | } else if (Env1.getValue(Loc: *SynthFieldLoc1) != |
125 | Env2.getValue(Loc: Loc2.getSyntheticField(Name))) { |
126 | return false; |
127 | } |
128 | } |
129 | |
130 | return true; |
131 | } |
132 | |
133 | } // namespace clang::dataflow |
134 | |