mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
C++: IR support for range-based for loops
IR construction was missing support for C++ 11 range-based `for` loops. The extractor generates ASTs for the compiler-generated implementation already, so I had enough information to generate IR. I've expanded on some of the predicates in `RangeBasedForStmt` to access the desugared information. One complication was that the `DeclStmt`s for the compiler-generated variables seem to have results for `getDeclaration()` but not for `getDeclarationEntry()`. This required handling these slightly differently than we do for other `DeclStmt`s. The flow for range-based `for` is actually easier than for a regular `for`, because all three components (init, condition, and update) are always present.
This commit is contained in:
@@ -7427,3 +7427,146 @@ ir.cpp:
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1045, col. 23 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1050| vector<int>& vector<int>::operator=(vector<int> const&)
|
||||
# 1050| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const vector<int> &
|
||||
# 1050| vector<int>& vector<int>::operator=(vector<int>&&)
|
||||
# 1050| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = vector<int> &&
|
||||
# 1051| vector<int>::iterator& vector<int>::iterator::operator=(vector<int>::iterator const public&)
|
||||
# 1051| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const iterator &
|
||||
# 1051| vector<int>::iterator& vector<int>::iterator::operator=(vector<int>::iterator&&)
|
||||
# 1051| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = iterator &&
|
||||
# 1053| vector<T>::iterator& vector<T>::iterator::operator++()
|
||||
# 1053| params:
|
||||
# 1053| vector<int>::iterator& vector<int>::iterator::operator++()
|
||||
# 1053| params:
|
||||
# 1054| T& vector<T>::iterator::operator*() const
|
||||
# 1054| params:
|
||||
# 1054| int& vector<int>::iterator::operator*() const
|
||||
# 1054| params:
|
||||
# 1056| bool vector<T>::iterator::operator!=(vector<T>::iterator) const
|
||||
# 1056| params:
|
||||
# 1056| 0: right
|
||||
# 1056| Type = iterator
|
||||
# 1056| bool vector<int>::iterator::operator!=(vector<int>::iterator) const
|
||||
# 1056| params:
|
||||
# 1056| 0: right
|
||||
# 1056| Type = iterator
|
||||
# 1059| vector<T>::iterator vector<T>::begin() const
|
||||
# 1059| params:
|
||||
# 1059| vector<int>::iterator vector<int>::begin() const
|
||||
# 1059| params:
|
||||
# 1060| vector<T>::iterator vector<T>::end() const
|
||||
# 1060| params:
|
||||
# 1060| vector<int>::iterator vector<int>::end() const
|
||||
# 1060| params:
|
||||
# 1064| bool operator==<T>(iterator, iterator)
|
||||
# 1064| params:
|
||||
# 1064| 0: left
|
||||
# 1064| Type = iterator
|
||||
# 1064| 1: right
|
||||
# 1064| Type = iterator
|
||||
# 1066| bool operator!=<T>(iterator, iterator)
|
||||
# 1066| params:
|
||||
# 1066| 0: left
|
||||
# 1066| Type = iterator
|
||||
# 1066| 1: right
|
||||
# 1066| Type = iterator
|
||||
# 1068| void RangeBasedFor(vector<int> const&)
|
||||
# 1068| params:
|
||||
# 1068| 0: v
|
||||
# 1068| Type = const vector<int> &
|
||||
# 1068| body: { ... }
|
||||
# 1069| 0: for(...:...) ...
|
||||
# 1069| 0: declaration
|
||||
# 1069| 1: declaration
|
||||
# 1069| 2: call to operator!=
|
||||
# 1069| Type = bool
|
||||
# 1069| ValueCategory = prvalue
|
||||
#-----| -1: (const iterator)...
|
||||
#-----| Conversion = glvalue conversion
|
||||
#-----| Type = const iterator
|
||||
#-----| ValueCategory = lvalue
|
||||
#-----| expr: (__begin)
|
||||
#-----| Type = iterator
|
||||
#-----| ValueCategory = lvalue
|
||||
#-----| 0: (__end)
|
||||
#-----| Type = iterator
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1069| 3: (reference dereference)
|
||||
# 1069| Type = iterator
|
||||
# 1069| ValueCategory = lvalue
|
||||
# 1069| expr: call to operator++
|
||||
# 1069| Type = iterator &
|
||||
# 1069| ValueCategory = prvalue
|
||||
#-----| -1: (__begin)
|
||||
#-----| Type = iterator
|
||||
#-----| ValueCategory = lvalue
|
||||
# 1069| 4: declaration
|
||||
# 1069| 5: { ... }
|
||||
# 1070| 0: if (...) ...
|
||||
# 1070| 0: ... > ...
|
||||
# 1070| Type = bool
|
||||
# 1070| ValueCategory = prvalue
|
||||
# 1070| 0: e
|
||||
# 1070| Type = int
|
||||
# 1070| ValueCategory = prvalue(load)
|
||||
# 1070| 1: 0
|
||||
# 1070| Type = int
|
||||
# 1070| Value = 0
|
||||
# 1070| ValueCategory = prvalue
|
||||
# 1070| 1: { ... }
|
||||
# 1071| 0: continue;
|
||||
# 1069| 1: label ...:
|
||||
# 1075| 1: for(...:...) ...
|
||||
# 1075| 0: declaration
|
||||
# 1075| 1: declaration
|
||||
# 1075| 2: call to operator!=
|
||||
# 1075| Type = bool
|
||||
# 1075| ValueCategory = prvalue
|
||||
#-----| -1: (const iterator)...
|
||||
#-----| Conversion = glvalue conversion
|
||||
#-----| Type = const iterator
|
||||
#-----| ValueCategory = lvalue
|
||||
#-----| expr: (__begin)
|
||||
#-----| Type = iterator
|
||||
#-----| ValueCategory = lvalue
|
||||
#-----| 0: (__end)
|
||||
#-----| Type = iterator
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1075| 3: (reference dereference)
|
||||
# 1075| Type = iterator
|
||||
# 1075| ValueCategory = lvalue
|
||||
# 1075| expr: call to operator++
|
||||
# 1075| Type = iterator &
|
||||
# 1075| ValueCategory = prvalue
|
||||
#-----| -1: (__begin)
|
||||
#-----| Type = iterator
|
||||
#-----| ValueCategory = lvalue
|
||||
# 1075| 4: declaration
|
||||
# 1075| 5: { ... }
|
||||
# 1076| 0: if (...) ...
|
||||
# 1076| 0: ... < ...
|
||||
# 1076| Type = bool
|
||||
# 1076| ValueCategory = prvalue
|
||||
# 1076| 0: (reference dereference)
|
||||
# 1076| Type = int
|
||||
# 1076| ValueCategory = prvalue(load)
|
||||
# 1076| expr: e
|
||||
# 1076| Type = const int &
|
||||
# 1076| ValueCategory = prvalue(load)
|
||||
# 1076| 1: 5
|
||||
# 1076| Type = int
|
||||
# 1076| Value = 5
|
||||
# 1076| ValueCategory = prvalue
|
||||
# 1076| 1: { ... }
|
||||
# 1077| 0: break;
|
||||
# 1079| 2: label ...:
|
||||
# 1080| 3: return ...
|
||||
|
||||
@@ -1046,6 +1046,39 @@ void Lambda(int x, const String& s) {
|
||||
lambda_inits(6);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct vector {
|
||||
struct iterator {
|
||||
T* p;
|
||||
iterator& operator++();
|
||||
T& operator*() const;
|
||||
|
||||
bool operator!=(iterator right) const;
|
||||
};
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool operator==(typename vector<T>::iterator left, typename vector<T>::iterator right);
|
||||
template<typename T>
|
||||
bool operator!=(typename vector<T>::iterator left, typename vector<T>::iterator right);
|
||||
|
||||
void RangeBasedFor(const vector<int>& v) {
|
||||
for (int e : v) {
|
||||
if (e > 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (const int& e : v) {
|
||||
if (e < 5) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // Explicit capture of `this` requires possible extractor fixes.
|
||||
|
||||
struct LambdaContainer {
|
||||
|
||||
@@ -4861,3 +4861,140 @@ ir.cpp:
|
||||
# 1045| v0_29(void) = ReturnValue : &:r0_28, ~mu0_2
|
||||
# 1045| v0_30(void) = UnmodeledUse : mu*
|
||||
# 1045| v0_31(void) = ExitFunction :
|
||||
|
||||
# 1068| void RangeBasedFor(vector<int> const&)
|
||||
# 1068| Block 0
|
||||
# 1068| v0_0(void) = EnterFunction :
|
||||
# 1068| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1068| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1068| r0_3(glval<vector<int> &>) = VariableAddress[v] :
|
||||
# 1068| mu0_4(vector<int> &) = InitializeParameter[v] : &:r0_3
|
||||
# 1069| r0_5(glval<vector<int> &>) = VariableAddress[(__range)] :
|
||||
# 1069| r0_6(glval<vector<int> &>) = VariableAddress[v] :
|
||||
# 1069| r0_7(vector<int> &) = Load : &:r0_6, ~mu0_2
|
||||
# 1069| mu0_8(vector<int> &) = Store : &:r0_5, r0_7
|
||||
# 1069| r0_9(glval<iterator>) = VariableAddress[(__begin)] :
|
||||
#-----| r0_10(glval<vector<int> &>) = VariableAddress[(__range)] :
|
||||
#-----| r0_11(vector<int> &) = Load : &:r0_10, ~mu0_2
|
||||
# 1069| r0_12(glval<unknown>) = FunctionAddress[begin] :
|
||||
# 1069| r0_13(iterator) = Call : func:r0_12, this:r0_11
|
||||
# 1069| mu0_14(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1069| mu0_15(iterator) = Store : &:r0_9, r0_13
|
||||
# 1069| r0_16(glval<iterator>) = VariableAddress[(__end)] :
|
||||
#-----| r0_17(glval<vector<int> &>) = VariableAddress[(__range)] :
|
||||
#-----| r0_18(vector<int> &) = Load : &:r0_17, ~mu0_2
|
||||
# 1069| r0_19(glval<unknown>) = FunctionAddress[end] :
|
||||
# 1069| r0_20(iterator) = Call : func:r0_19, this:r0_18
|
||||
# 1069| mu0_21(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1069| mu0_22(iterator) = Store : &:r0_16, r0_20
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
#-----| Block 1
|
||||
#-----| r1_0(glval<iterator>) = VariableAddress[(__begin)] :
|
||||
#-----| r1_1(glval<iterator>) = Convert : r1_0
|
||||
# 1069| r1_2(glval<unknown>) = FunctionAddress[operator!=] :
|
||||
#-----| r1_3(glval<iterator>) = VariableAddress[(__end)] :
|
||||
#-----| r1_4(iterator) = Load : &:r1_3, ~mu0_2
|
||||
# 1069| r1_5(bool) = Call : func:r1_2, this:r1_1, 0:r1_4
|
||||
# 1069| mu1_6(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1069| v1_7(void) = ConditionalBranch : r1_5
|
||||
#-----| False -> Block 5
|
||||
#-----| True -> Block 2
|
||||
|
||||
# 1069| Block 2
|
||||
# 1069| r2_0(glval<int>) = VariableAddress[e] :
|
||||
#-----| r2_1(glval<iterator>) = VariableAddress[(__begin)] :
|
||||
#-----| r2_2(glval<iterator>) = Convert : r2_1
|
||||
# 1069| r2_3(glval<unknown>) = FunctionAddress[operator*] :
|
||||
# 1069| r2_4(int &) = Call : func:r2_3, this:r2_2
|
||||
# 1069| mu2_5(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1069| r2_6(int) = Load : &:r2_4, ~mu0_2
|
||||
# 1069| mu2_7(int) = Store : &:r2_0, r2_6
|
||||
# 1070| r2_8(glval<int>) = VariableAddress[e] :
|
||||
# 1070| r2_9(int) = Load : &:r2_8, ~mu0_2
|
||||
# 1070| r2_10(int) = Constant[0] :
|
||||
# 1070| r2_11(bool) = CompareGT : r2_9, r2_10
|
||||
# 1070| v2_12(void) = ConditionalBranch : r2_11
|
||||
#-----| False -> Block 4
|
||||
#-----| True -> Block 3
|
||||
|
||||
# 1071| Block 3
|
||||
# 1071| v3_0(void) = NoOp :
|
||||
#-----| Goto -> Block 4
|
||||
|
||||
# 1069| Block 4
|
||||
# 1069| v4_0(void) = NoOp :
|
||||
#-----| r4_1(glval<iterator>) = VariableAddress[(__begin)] :
|
||||
# 1069| r4_2(glval<unknown>) = FunctionAddress[operator++] :
|
||||
# 1069| r4_3(iterator &) = Call : func:r4_2, this:r4_1
|
||||
# 1069| mu4_4(unknown) = ^CallSideEffect : ~mu0_2
|
||||
#-----| Goto (back edge) -> Block 1
|
||||
|
||||
# 1075| Block 5
|
||||
# 1075| r5_0(glval<vector<int> &>) = VariableAddress[(__range)] :
|
||||
# 1075| r5_1(glval<vector<int> &>) = VariableAddress[v] :
|
||||
# 1075| r5_2(vector<int> &) = Load : &:r5_1, ~mu0_2
|
||||
# 1075| mu5_3(vector<int> &) = Store : &:r5_0, r5_2
|
||||
# 1075| r5_4(glval<iterator>) = VariableAddress[(__begin)] :
|
||||
#-----| r5_5(glval<vector<int> &>) = VariableAddress[(__range)] :
|
||||
#-----| r5_6(vector<int> &) = Load : &:r5_5, ~mu0_2
|
||||
# 1075| r5_7(glval<unknown>) = FunctionAddress[begin] :
|
||||
# 1075| r5_8(iterator) = Call : func:r5_7, this:r5_6
|
||||
# 1075| mu5_9(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1075| mu5_10(iterator) = Store : &:r5_4, r5_8
|
||||
# 1075| r5_11(glval<iterator>) = VariableAddress[(__end)] :
|
||||
#-----| r5_12(glval<vector<int> &>) = VariableAddress[(__range)] :
|
||||
#-----| r5_13(vector<int> &) = Load : &:r5_12, ~mu0_2
|
||||
# 1075| r5_14(glval<unknown>) = FunctionAddress[end] :
|
||||
# 1075| r5_15(iterator) = Call : func:r5_14, this:r5_13
|
||||
# 1075| mu5_16(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1075| mu5_17(iterator) = Store : &:r5_11, r5_15
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
#-----| Block 6
|
||||
#-----| r6_0(glval<iterator>) = VariableAddress[(__begin)] :
|
||||
#-----| r6_1(glval<iterator>) = Convert : r6_0
|
||||
# 1075| r6_2(glval<unknown>) = FunctionAddress[operator!=] :
|
||||
#-----| r6_3(glval<iterator>) = VariableAddress[(__end)] :
|
||||
#-----| r6_4(iterator) = Load : &:r6_3, ~mu0_2
|
||||
# 1075| r6_5(bool) = Call : func:r6_2, this:r6_1, 0:r6_4
|
||||
# 1075| mu6_6(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1075| v6_7(void) = ConditionalBranch : r6_5
|
||||
#-----| False -> Block 10
|
||||
#-----| True -> Block 8
|
||||
|
||||
#-----| Block 7
|
||||
#-----| r7_0(glval<iterator>) = VariableAddress[(__begin)] :
|
||||
# 1075| r7_1(glval<unknown>) = FunctionAddress[operator++] :
|
||||
# 1075| r7_2(iterator &) = Call : func:r7_1, this:r7_0
|
||||
# 1075| mu7_3(unknown) = ^CallSideEffect : ~mu0_2
|
||||
#-----| Goto (back edge) -> Block 6
|
||||
|
||||
# 1075| Block 8
|
||||
# 1075| r8_0(glval<int &>) = VariableAddress[e] :
|
||||
#-----| r8_1(glval<iterator>) = VariableAddress[(__begin)] :
|
||||
#-----| r8_2(glval<iterator>) = Convert : r8_1
|
||||
# 1075| r8_3(glval<unknown>) = FunctionAddress[operator*] :
|
||||
# 1075| r8_4(int &) = Call : func:r8_3, this:r8_2
|
||||
# 1075| mu8_5(unknown) = ^CallSideEffect : ~mu0_2
|
||||
# 1075| r8_6(glval<int>) = Convert : r8_4
|
||||
# 1075| mu8_7(int &) = Store : &:r8_0, r8_6
|
||||
# 1076| r8_8(glval<int &>) = VariableAddress[e] :
|
||||
# 1076| r8_9(int &) = Load : &:r8_8, ~mu0_2
|
||||
# 1076| r8_10(int) = Load : &:r8_9, ~mu0_2
|
||||
# 1076| r8_11(int) = Constant[5] :
|
||||
# 1076| r8_12(bool) = CompareLT : r8_10, r8_11
|
||||
# 1076| v8_13(void) = ConditionalBranch : r8_12
|
||||
#-----| False -> Block 7
|
||||
#-----| True -> Block 9
|
||||
|
||||
# 1077| Block 9
|
||||
# 1077| v9_0(void) = NoOp :
|
||||
#-----| Goto -> Block 10
|
||||
|
||||
# 1079| Block 10
|
||||
# 1079| v10_0(void) = NoOp :
|
||||
# 1080| v10_1(void) = NoOp :
|
||||
# 1068| v10_2(void) = ReturnVoid :
|
||||
# 1068| v10_3(void) = UnmodeledUse : mu*
|
||||
# 1068| v10_4(void) = ExitFunction :
|
||||
|
||||
Reference in New Issue
Block a user