Extend the alpha.core.Conversion checker to handle implicit converions
where a too large integer value is converted to a floating point type. Each
floating point type has a range where it can exactly represent all integers; we
emit a warning when the integer value is above this range. Although it is
possible to exactly represent some integers which are outside of this range
(those that are divisible by a large enough power of 2); we still report cast
involving those, because their usage may lead to bugs. (For example, if 1<<24
is stored in a float variable x, then x==x+1 holds.)
Details
Diff Detail
- Repository
- rC Clang
Event Timeline
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
176 | I'm actually not too sure about this. @whisperity? |
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
176 | Yeah, i suspect it's a host machine check (to prevent our own overflow on line 189) rather than a target machine check. |
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
176 | Oh right, thanks for clarifying that. |
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
176 | Yes, this is intended to be a host machine check (corresponding to the W >= 64U in the old version). I will add a comment to clarify this. |
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
163 | A link to the source of these number would be useful. How are these calculated. Also, as far as I know the current C++ standard does not require anything about how floating points are represented in an implementation. So it would be great to somehow refer to the representation used by clang rather than hardcoding these values. (Note that I am perfectly fine with warning for implementation defined behavior as the original version also warn for such in case of integers.) | |
171 | Comment should be full sentences with a full stop at the end. | |
172 | Do not add redundant braces when we only guard one line. It is perfectly ok when the one statements spans over multiple lines (maybe due to comments). |
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
163 | I took these magic numbers from the IEEE 754 standard; I completely agree that their introduction is far from being elegant. Unfortunately it seems that referring to the representation used by clang seems to be somewhat difficult, see e.g. this old stackoverflow answer. In the Z3 solver a similar problem was solved by defining a static function (getFloatSemantics()) which uses a switch to translate the bit width of a floating point type into an llvm::fltSemantics value (which contains the precision value as a field). I could imagine three solutions:
Which of these is the best? Note: According to the documentation the floating point types supported by the LLVM IR are just float, double and some high precision extension types (which are handled by the default: branch of my code). Unfortunately, I do not know what happens to the [[ https://clang.llvm.org/docs/LanguageExtensions.html#half-precision-floating-point | _Float16 ]] half-width float type. |
Give a reference for the significand size values of the IEEE 754 floating point types; cleanup comments and formatting.
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
163 | I updated the patch with a comment describing my assumptions, but I will implement a different solution if that would be better. |
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
164 | Continuing the float semantics discussion on the new revision - Did you consider llvm::APFloat? (http://llvm.org/doxygen/classllvm_1_1APFloat.html) This looks like something that you're trying to re-implement. |
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
164 | This switch statement essentially fulfills two functions: it maps QualTypes to standardized IEEE floating point types and it immediately maps the standardized IEEE types to their precision values. The difficulty is that I don't think that the first map is available as a public function in the clang libraries. This means that although a switch over the bit width of the floating point type is certainly inelegant, I cannot avoid it. The second map is available in the APFloat library (via the llvm::fltSemantics constants, which describe the standard IEEE types). However, this map is extremely stable (e.g. I don't think that the binary structure of the IEEE double type will ever change), so I think that re-implementing it is justified by the fact that it yields shorter and cleaner code. However, as I had noted previously, I am open to using the llvm::fltSemantics constants to implement this mapping. As an additional remark, my current code supports the _Float16 type, which is currently not supported by the APFloat/fltSemantics library. If we decide to use the llvm::fltSemantics constants, then we must either extend the APFloat library or apply some workarounds for this case. |
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
164 |
fltSemantics are not just enum constants, they contain a lot of fancy data: static const fltSemantics semIEEEhalf = {15, -14, 11, 16}; static const fltSemantics semIEEEsingle = {127, -126, 24, 32}; static const fltSemantics semIEEEdouble = {1023, -1022, 53, 64}; static const fltSemantics semIEEEquad = {16383, -16382, 113, 128}; static const fltSemantics semX87DoubleExtended = {16383, -16382, 64, 80}; |
Use APFloat to determine precision of floating point type
Additionally, fix a typo in the tests.
LGTM!
I only wonder if this should be on by default or guarded by a config option. I do not have strong feelings about any of the options though.
I found a solution for determining the precision value of a floating point type. Is this acceptable?
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
164 | I knew about the data members of fltSemantics, but I did not see a way to use them elegantly (although fltSemantics has sizeInBits as a member, the possible fltSemantics values are not collected into one data structure, so I could not look up the precision field). However, as I examined the APFloat.h source again, I found the function llvm::APFloat::getAllOnesValue which allows me to map bit width to semantics. While this is a somewhat indirect solution, it avoids using a switch. The `missing _Float16' problem mentioned in my previous comments was my mistake, somehow I managed to overlook semIEEEhalf. |
The new addition makes perfect sense, please feel free to commit :) Eventually i guess we'll need to declare where is this checker going and what specific lint rule is this checker designed to check, document this rule, and move it to optin once it has no false positives with respect to that rule and warning messages are improved. I suspect that it would need trackNullOrUndefValue() aka trackExpressionValue() and the currently discussed constraint tracking so that to explain why does the checker thinks that the input value of the cast is constrained to be losing precision on the current path.
I only wonder if this should be on by default or guarded by a config option.
Off-by-default flags mostly make sense when the newly added feature is somehow more opt-in than the rest of the checker, and for now i don't see any indication of that. If it turns out that this part of the checker has its own specific problems, i guess we can put it behind a flag, otherwise kinda why bother?
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
158–162 | Hmm, so the remaining problem is how to extract float semantics from a float QualType? Would ASTContext::getFloatTypeSemantics(DestType) make sense? | |
test/Analysis/conversion.c | ||
193 | Tests are often unformatted and we usually don't care. Additionally, they are almost always violating LLVM's camelCase vs. under_score conventions. |
Could someone with commit rights commit this patch (if it is acceptable)? I don't have commit rights myself.
lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | ||
---|---|---|
158–162 | Thank you, that is the method I was looking for! |
test/Analysis/conversion.c | ||
---|---|---|
193 | Hmm, okay :) |
Hmm, so the remaining problem is how to extract float semantics from a float QualType? Would ASTContext::getFloatTypeSemantics(DestType) make sense?