ASDenysPetrov added inline comments.

================
Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:358
+  assert(!isEmpty());
+  return begin()->From().isUnsigned();
+}
----------------
martong wrote:
> Probably it is unrelated to this patch, but
> Could it happen that `(++begin())->From().isUnsigned()` gives a different 
> signedness? Or we had a separate assertion when we  added the second `Range`? 
> The same question applies to the below two functions as well. Seems like in 
> `unite` we don't have such validity check...
>Or we had a separate assertion when we added the second Range?
I didn't find any assertion while adding.
Moreover, I'm not sure if there can happen `APSInts` of different types in a 
single `Range`. We only have `assert(From < To)` in a ctor.
>Seems like in `unite` we don't have such validity check...
Not only `unite` doesn't have such, but all the rest don't as well. We rely on 
a fact that the caller garantees the validity of RangeSet by using 
`AdjustmentType`.
But, yes, it's worth to make some checks.


================
Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:687
+
+  if (IsConversion && (!IsPromotion || !What.isUnsigned()))
+    return makePersistent(convertTo(What, Ty));
----------------
martong wrote:
> Could you please explain why do we need the `|| !What.isUnsigned()` part of 
> the condition?
> 
> This would make much more sense to me:
> ```
> if (IsConversion && !IsPromotion)
>     return makePersistent(convertTo(What, Ty));
> ```
Look. Here we handle 2 cases:

  - `IsConversion && !IsPromotion`. In this case we handle changing a sign with 
same bitwidth: char -> uchar, uint -> int. Here we convert //negatives //to 
//positives //and //positives which is out of range// to //negatives//. We use 
`convertTo` function for that.
 
  - `IsConversion && IsPromotion && !What.isUnsigned()`. In this case we handle 
changing a sign from signeds to unsigneds with higher bitwidth: char -> uint, 
int-> uint64. The point is that we also need convert //negatives //to 
//positives //and use `convertTo` function as well. For example, we don't need 
such a convertion when converting //unsigned //to //signed with higher 
bitwidth//, because all the values of //unsigned //is valid for the such 
//signed//.
     





================
Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:690
+
+  // Promotion from unsigneds to signeds/unsigneds left.
+
----------------
martong wrote:
> martong wrote:
> > I think it would be more consistent with the other operations (truncate and 
> > convert) if we had this logic in its own separate function: `promoteTo`. 
> > And this function should be called only if `isPromotion` is set (?)
> This comment is confusing, since we can get here also if
> `isConversion` is false and
> `isPromotion` is false 
Nothing confusing is here.
We have 7 main cases:
NOOP: **u -> u, s -> s**
Conversion: **u -> s, s -> u**
Truncation: **U-> u, S -> s**
Promotion: `u->U`, `s -> S`
Truncation + Conversion: **U -> s, S -> u**
Promotion + Conversion: **s -> U**, `u -> S`

As you can see, we've handled all the **bolds** above this line .
So only promotions from //unsigneds// left. That means, `isPromotion` never 
should be `false` here. We could add an assertion here if you want.


================
Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:690-711
+  // Promotion from unsigneds to signeds/unsigneds left.
+
+  using llvm::APInt;
+  using llvm::APSInt;
+  ContainerType Result;
+  // We definitely know the size of the result set.
+  Result.reserve(What.size());
----------------
ASDenysPetrov wrote:
> martong wrote:
> > martong wrote:
> > > I think it would be more consistent with the other operations (truncate 
> > > and convert) if we had this logic in its own separate function: 
> > > `promoteTo`. And this function should be called only if `isPromotion` is 
> > > set (?)
> > This comment is confusing, since we can get here also if
> > `isConversion` is false and
> > `isPromotion` is false 
> Nothing confusing is here.
> We have 7 main cases:
> NOOP: **u -> u, s -> s**
> Conversion: **u -> s, s -> u**
> Truncation: **U-> u, S -> s**
> Promotion: `u->U`, `s -> S`
> Truncation + Conversion: **U -> s, S -> u**
> Promotion + Conversion: **s -> U**, `u -> S`
> 
> As you can see, we've handled all the **bolds** above this line .
> So only promotions from //unsigneds// left. That means, `isPromotion` never 
> should be `false` here. We could add an assertion here if you want.
That makes sense. I'll do.


================
Comment at: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:792
+  //    unite -> uchar(-2, 1)
+  auto CastRange = [Ty, &VF = ValueFactory](const Range &R) -> Bounds {
+    // Get bounds of the given range.
----------------
martong wrote:
> Why not `-> Range`?
Because `Range` requires `from < to` in its contract. But I need to store the 
values after conversions. You can see a check `if (NewBounds.first > 
NewBounds.second)` on line #812.


================
Comment at: clang/unittests/StaticAnalyzer/RangeSetTest.cpp:827
+  if (is_signed_v<F>) {
+    this->template checkCastTo<F, T>({{MIN, MIN}, {MAX, MAX}}, {{MAX, MIN}});
+    this->template checkCastTo<F, T>({{MID, MID}, {MAX, MAX}}, {{MAX, MID}});
----------------
martong wrote:
> I am getting lost. Why don't you check against `ToMIN` and `ToMAX` here? 
> Could you explain e.g. with int16->int8?
> 
> It is confusing that at many places you test against `MIN`, `MAX`, `A`, ... 
> and the conversion is happening automatically by the `checkCastTo` template. 
> Would it be more explicit to use everywhere `ToMIN`, `ToA`, `ToB`, ... and 
> check against them?
We can't use ToMIN, ToMAX, ... everywhere. That would be incorrect:
**int16(-32768, 32767)** -> **int8(-128, 127)**, aka `(MIN, MAX) -> (ToMIN, 
ToMAX) // OK`.
**int16(-32768, -32768)** -> **int8(-128, -128)**, aka `(MIN, MIN) -> (ToMIN, 
ToMIN) // NOK`.
**int16(-32768,-32768)** -> **int8(0, 0)**, aka `(MIN, MIN) -> ((int8)MIN, 
(int8)MIN) // OK`.



================
Comment at: clang/unittests/StaticAnalyzer/RangeSetTest.cpp:874-879
+  constexpr auto FromA = TV::FromA;
+  constexpr auto ToA = TV::ToA;
+  constexpr auto FromB = TV::FromB;
+  constexpr auto ToB = TV::ToB;
+  this->template checkCastTo<F, T>({{FromA, ToA}, {FromB, ToB}},
+                                   {{FromA, ToA}});
----------------
martong wrote:
> Could you please elaborate what do we test here?
E.g. 
```
                 RangeA                   U                   RangeB
(0000'1111'0000'0100, 0000'1111'0000'0111)U(1111'0000'0000'0100, 
1111'0000'0000'0111)
truncates to
                 RangeC                   U                   RangeC
(          0000'0100,           0000'0111)U(          0000'0100,           
0000'0111)
As you can see, we got two equal truncated ranges.
                           RangeC
Then unite them to (0000'0100, 0000'0111).
```


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D103094/new/

https://reviews.llvm.org/D103094

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to