
This reverts commit f02c5f3478318075d1a469203900e452ba651421 and addresses the issue mentioned in D114619 differently. Repeating the issue here: Currently, during symbol simplification we remove the original member symbol from the equivalence class (`ClassMembers` trait). However, we keep the reverse link (`ClassMap` trait), in order to be able the query the related constraints even for the old member. This asymmetry can lead to a problem when we merge equivalence classes: ``` ClassA: [a, b] // ClassMembers trait, a->a, b->a // ClassMap trait, a is the representative symbol ``` Now let,s delete `a`: ``` ClassA: [b] a->a, b->a ``` Let's merge ClassA into the trivial class `c`: ``` ClassA: [c, b] c->c, b->c, a->a ``` Now, after the merge operation, `c` and `a` are actually in different equivalence classes, which is inconsistent. This issue manifests in a test case (added in D103317): ``` void recurring_symbol(int b) { if (b * b != b) if ((b * b) * b * b != (b * b) * b) if (b * b == 1) } ``` Before the simplification we have these equivalence classes: ``` trivial EQ1: [b * b != b] trivial EQ2: [(b * b) * b * b != (b * b) * b] ``` During the simplification with `b * b == 1`, EQ1 is merged with `1 != b` `EQ1: [b * b != b, 1 != b]` and we remove the complex symbol, so `EQ1: [1 != b]` Then we start to simplify the only symbol in EQ2: `(b * b) * b * b != (b * b) * b --> 1 * b * b != 1 * b --> b * b != b` But `b * b != b` is such a symbol that had been removed previously from EQ1, thus we reach the above mentioned inconsistency. This patch addresses the issue by making it impossible to synthesise a symbol that had been simplified before. We achieve this by simplifying the given symbol to the absolute simplest form. Differential Revision: https://reviews.llvm.org/D114887
41 lines
1.4 KiB
C++
41 lines
1.4 KiB
C++
// RUN: %clang_analyze_cc1 %s \
|
|
// RUN: -analyzer-checker=core \
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
// RUN: 2>&1 | FileCheck %s
|
|
|
|
// In this test we check whether the solver's symbol simplification mechanism
|
|
// is capable of reaching a fixpoint. This should be done after one iteration.
|
|
|
|
void clang_analyzer_printState();
|
|
|
|
void test(int a, int b, int c) {
|
|
if (a + b != c)
|
|
return;
|
|
clang_analyzer_printState();
|
|
// CHECK: "constraints": [
|
|
// CHECK-NEXT: { "symbol": "((reg_$0<int a>) + (reg_$1<int b>)) != (reg_$2<int c>)", "range": "{ [0, 0] }" }
|
|
// CHECK-NEXT: ],
|
|
// CHECK-NEXT: "equivalence_classes": [
|
|
// CHECK-NEXT: [ "(reg_$0<int a>) + (reg_$1<int b>)", "reg_$2<int c>" ]
|
|
// CHECK-NEXT: ],
|
|
// CHECK-NEXT: "disequality_info": null,
|
|
|
|
// Simplification starts here.
|
|
if (b != 0)
|
|
return;
|
|
clang_analyzer_printState();
|
|
// CHECK: "constraints": [
|
|
// CHECK-NEXT: { "symbol": "(reg_$0<int a>) != (reg_$2<int c>)", "range": "{ [0, 0] }" },
|
|
// CHECK-NEXT: { "symbol": "reg_$1<int b>", "range": "{ [0, 0] }" }
|
|
// CHECK-NEXT: ],
|
|
// CHECK-NEXT: "equivalence_classes": [
|
|
// CHECK-NEXT: [ "(reg_$0<int a>) != (reg_$2<int c>)" ],
|
|
// CHECK-NEXT: [ "reg_$0<int a>", "reg_$2<int c>" ]
|
|
// CHECK-NEXT: ],
|
|
// CHECK-NEXT: "disequality_info": null,
|
|
|
|
// Keep the symbols and the constraints! alive.
|
|
(void)(a * b * c);
|
|
return;
|
|
}
|