12 ms·
GCC should warn about 2^16 and 2^32 and 2^64
- ChrisRR 7y agoFor those who aren't C/C++ programmers, the issue here is people using ^ to mean power where it means XOR in C/C++.
- fefe23 7y agoEven if it meant power, it would make sense to warn here, because storing 2^16 in an unsigned short yields 0 :-)
- cfstras 7y agothese are signed, so you‘re getting -2^16... so many warnings to emit! /edit: had that mixed up in my head. Interestingly, clang does warn about the overflow: main.c:6:17: warning: implicit conversion from 'int' to 'int16_t' (aka 'short') changes value from 65536 to 0 [-Wconstant-conversion] int16_t s16 = 65536;
- enedil 7y agoOops, you meant -2^15 I guess.
- NikkiA 7y agoNo, they meant -2^16, ie, -(0)
- majewsky 7y ago-2^16 is not -0. The unary minus binds more tightly than the binary xor, so this comes out as -2^16 = (-2) ^ 16 = 0b111...1110 ^ 0b10000 = 0b111...11011110 = -18 if I'm not mistaken.
- NikkiA 7y agoNo, we mean the power operator, 2 to the power of 16 would be '0' in a short int, since the value overflows... But since philosophically a signed int has '1's in every bit from the MSB to infinity towards the left, it should thus be -0 not 0.
- leereeves 7y agoIn two's complement (the usual binary representation of signed ints), -0 is the same as 0.
- garmaine 7y agoYeah my own first reaction was “Oh, gcc doesn’t warn about overflow assignment? That’s a definite bug.” I code in C/C++ too, although not exclusively. I facepalmed a few seconds later, but it goes to show the value of having this be a compiler warning.
- quickthrower2 7y agoI am not a C programmer but my opinion with any language is don’t try to catch mistakes like this in the compiler. Instead leave to configurable and competing linters for people who want to catch possible mistakes like this. Anyone caught by this mistake probably hasn’t tested their program adequately. Any mistake induced by the wrong constant number should be easy to weed out with unit testing.
- scatters 7y agoBy the time you write a linter, you've done more than half of the work of a compiler, at which point you might as well just ship it as a compiler. It's a bit different for clang, which is modular and provides libraries that tool writers can use, but gcc is intentionally monolithic.
- gchadwick 7y ago> By the time you write a linter, you've done more than half of the work of a compiler, at which point you might as well just ship it as a compiler. Does depend on what kind of lint you want to catch but something like this 'just' needs some lexing and parsing to give a usable AST that's reasonably clean so it's not too hard to write lint rules against it. (Not trivial for C & C++ hence 'just') Building the full compiler is much more work, especially if you want a decent optimizing compiler.
- barrkel 7y agoThe back end is a lot of work, but so is the front end in a language like C++. In fact most of the essential complexity is in the front end; you make do with a simple back end, but the front end is irreducibly complex if you want it to actually compile the language to spec. And you need the front end to lint, especially if you want to avoid false positives, the bane of linting tools.
- zaarn 7y agoAnd better hope you didn't put in any bugs that GCC does or doesn't have, then you're left helpless as linter and compiler engage in epic battle on who is right.
- dehrmann 7y agoAnd Java, Python, Javascript, Ruby. The list goes on. It's more of a general problem for non-programmers than specifically people who are new to C (unlike the j++ in python someone mentioned).
- phaedrus 7y agoI am a C++ programmer and yet I also failed to catch on that ^ in the ticket was xor not pow until I read the comments. All it took was being a bit sleepy. A rather unfortunate operator choice on the part of C: open to confusion and precluded adding ^ as an exponentiation operator to the language.
- AKrumbach 7y agoWell, that isn't the only bug in the code, just the one which is being requested for prompting the warning. It's been about a decade since I've written significant C/C++ code, but something far more dangerous stood out to me: Upon first reading, I'd assume they are seeing the maximum unsigned values, but their variables aren't declared as unsigned -- and C/C++ default to signed values. Secondly, representing the nth power of two actually takes n+1 binary digits, so e.g. 28 is a one, followed by 8 zeros -- overflowing any 8-bit variable, signed or unsigned! This is undefined behavior as-declared (signed values), but would be entered as a zero for explicitly declared unsigned variables. Personally, if I were to be implementing this, I'd use bitwise operations to generate the correct values, and explicitly acknowledge in a comment something like "the following code knowingly breaks the abstraction of numeric values. It is likely not portable to other hardware, nor should you assume easy extension to other variable types. Das Quellcode ist nicht fur der gefingerpoken!"
- NotPaidToPost 7y agoI don't think it would not be simple or a good idea to warn because these are perfectly valid expressions and it would lead to a flood of warnings if compilers started to display "Did you really mean that?" for an ever growing list of expressions (which is what it would become). Better to leave this to code analysis tools. Edit: 2^32 uses the ^ operator exactly as intended, and has perfectly legitimate uses, especially when dealing with registers of HW peripherals. It's not the same as warning on "if (a=b)" as someone replied. From a language perspective, 2^32 is exactly the same as 2+32.
- rocqua 7y agoIt's a warning not an error. Moreover, it is very hard to come up with a legitimate usecase of 2^32 as an expression. People who use it to set bit flags will use &, and for bit flags, octal or hex notation is probably better.
- NotPaidToPost 7y ago> It's a warning not an error. Many build environments are set to treat all warnings as errors. The point is that 2^32 is a perfectly compliant C expression that is neither misleading nor ambiguous, and that also won't create any variable overflow. It uses ^ exactly as intended. Why should the compiler complain? Why should I get a warning/error when following the spec to the letter?
- lifthrasiir 7y agoLike it or not, there are already tons of lint-like warnings in GCC [1] and clang [2]. Many (uh, most?) of them are not required by the C/C++ standards (which by the way have their own requirements for diagnostic messages). [1] https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html [2] https://clang.llvm.org/docs/DiagnosticsReference.html https://clang.llvm.org/docs/DiagnosticsReference.html
- stestagg 7y agoEnvironments where the decision has been made that error-prone, misleading and ambiguous statements should not be allowed typically are those where warning are treated as errors. A new type of misleading and error-prone statement has been found, so it seems entirely reasonable that this is added to that list. This seems to be the entire intent of compiler warnings (these days anyway)
- ainar-g 7y agoDominik Honnef's staticcheck tool for the Go programming language will probably warn about these as well[1]. [1]: https://github.com/dominikh/go-tools/issues/516 https://github.com/dominikh/go-tools/issues/516.
- TorKlingberg 7y agoIt would be nice, but I can see it's not easy to come up with a warning heuristic that doesn't give a bunch of false positives. Sure, 2^16 is "obviously" wrong, but how about SOMETHING ^ XOR_KEY with #define SOMETHING and #define XOR_KEY 16?
- DaiPlusPlus 7y agoThey discuss that in the thread. Consensus seems to be to warn when seeing “2 ^ (certain base-10 literals)”. It’s uncommon to use base 10 when bit-twiddling with the XOR operator - so this makes sense. Of course, it would be better if C-family languages didn’t use ^ for XOR in the first place.
- mehrdadn 7y agoDo they not currently/do they plan to track macro expansion well enough to distinguish between macros and literals? I feel like they should be able to get the best of both worlds here.
- simias 7y ago>Of course, it would be better if C-family languages didn’t use ^ for XOR in the first place. I think at the time they were created there simply wasn't an ambiguity in anybody's mind. POW is a relatively complex operation, I don't think anybody expected it as a builtin operator (much like SQRT and friends). Arguably so is DIV but I suppose that's too convenient not to have. XOR on the other hand is something you really want to be a builtin operator in a low level, bit-twiddling language like C, so getting a dedicated operator makes sense. I'm not sure if an other sigil would've made more sense, after all ASCII lacks ⊕.
- dehrmann 7y ago> I think at the time they were created there simply wasn't an ambiguity in anybody's mind. And why would anyone in their right mind do 23 and not 1<<3? At least among the crowd who knows pow is expensive and what xor is.
- 7y ago
- antirez 7y agoTerrible idea. If a programmer is so unfamiliar with C/C++ to believe ^ is pow(), she/he will create a lot of other problems regardless. Without to mention the kind of bugs created by such incorrect usage would be simple to spot most of the cases.
- OskarS 7y agoWait... so your point is that since "bad programmers" do this, there shouldn't be a warning for it? That is among the silliest thing I ever heard. Especially since there's a trivial way to get rid of the warning: just write "18" instead of "2^16", if you REALLY intended the xor. Also: it's incredibly naive to think that "bad programmers" are the only ones that would make a mistake like this. I'm very familiar with all C/C++ operators and know perfectly well that the caret represents xor, but I could easily imagine myself slipping and writing 2^16 instead of 1 << 16, because that's how the rest of the world writes exponentials. Let he/she who has never written a typo and had the compiler save them cast the first stone.
- mehrdadn 7y ago> Wait... so your point is that since "bad programmers" do this, there shouldn't be a warning for it? That is among the silliest thing I ever heard. In all honesty while I don't agree with this particular instance, the reasoning isn't ridiculous. If we assume it's true that it'd only affect bad programmers, then you probably wouldn't want to add it, since it'd increase the false positive risk and/or otherwise potentially get in the way of everyone else. (Kind of like how you wouldn't make cars start honking when they're turned on as a means of protecting against bad drivers, even thought that might well save lives.) Some tools just need to assume some base level of expertise to be effective.
- OskarS 7y agoIf you want to make the argument "this is a bad warning because it will cause many false positives", then that's a perfectly valid argument to make. It doesn't really apply in this case, but it's a valid argument to make against a warning being added. That's not the reasoning given. The reasoning given is that since only bad programmers would make this mistake, it shouldn't be a warning at all, since those kinds of programmers "will create a lot of other problems regardless". That is nonsensical. You could make the same argument against ANY warning. The whole point of a warning is to alert the developers that, while their code technically follows the rules of the language, they've probably made a mistake.
- garethrees 7y agoThis seems like a good idea to me, since there's evidence that programmers are making this mistake, and the proposal is to add a warning only for the expressions that are most likely to be mistakes, that is 2^{decimal integer literal} and maybe also 10^{decimal integer literal}. There are many constructs that are well-defined in C, but where it is helpful to have warnings: use of = in condition expressions, implicit fallthrough in switch statements, misleading indentation, unsafe multiple statement macros, and so on. Programming in C is hard enough that every bit of compiler support is valuable.
- mrfredward 7y agoThe power vs xor mistake is no doubt a common one, but I take issue with the 2^8, 2^16, 2^32 examples that got singled out because they are also common bitmasks. Should FLAG_BIT1 ^ FLAG_BIT3 really be a warning? XOR may be rarely used, but 2^8 is not only one of the most common accidental XORs, it's also probably one of the more common legitimate uses of it.
- tln 7y ago> Should FLAG_BIT1 ^ FLAG_BIT3 really be a warning? Nope, the warning would be for literal numbers in the source. IMO anyone who doesn't define constants for the flag bits deserves the warning.
- mrfredward 7y agoSo 2^8 is a warning, but 2^BITS_IN_BYTE is not? I don't think whether or not the preprocessor helped in making the expression is a good heuristic for whether or not it is a mistake.
- garethrees 7y agoA warning heuristic needs to have a low false positive rate; a low false negative rate is nice but is not necessary. The purpose of a warning is to detect some common errors without inconveniencing too many correct programs. If some other errors go undetected then that is a shame but at least it is no worse than the current situation.
- nialv7 7y agoThis is an example of why you shouldn't use blanket warning options like -Wall in combination with -Werror. Otherwise your program might fail to compile just because the dev randomly thought of some new things to warn about.
- archi42 7y agoWe use -Wall and -Wextra, and fixed basically all our warnings. Now everyone writes virtually warning-free code (that is, any warning is fixed before committing). And I don't feel limited in what I can do. We could roll -Werror for most packages (I think we have a few packages, e.g. 3rd party libs, which emit warnings), but only use error=format and error=format-extra-args. To be fair, we disable these warnings (-Wno-...): unknown-pragmas, long-long, register, unused-command-line-argument.
- seba_dos1 7y agoI just keep my codebase warning free and check it in CI. No need for -Werror on package level, users don't need to deal with random breakage from a false-positive warning.
- archi42 7y agoIf you don't control the build env (e.g. FOSS work), then of course relying on -Werror might yield unnecessary trouble.
- marcosdumay 7y agoYou never completely control your build env. You are always relying on 3rd parties for some extent. That may not look relevant in short periods of time, but there are large odds that somebody will be biten by your choice of using -Werror 5 or 10 years down the road.
- archi42 7y agoI would not sign that claim as a general truth. It's true under some assumptions, but then, well, just disable -Werror in 5 to 10 years? It's not like THAT is a breaking change. OTOH 5 to 10 years is a good way to accumulate plenty of warnings (assuming a lack of development discipline), which at some point makes it easy to miss that one critical warning that should really be fixed. Maybe I should mention the worst case for an error in the code I write is "people die" (static analysis of safety critical stuff), so that's a good motivation to have no warnings ;-)
- oytis 7y agoWhat if I try to make a pipe with '|' or redirect the result with '>'? Should there be a warning for that? I find it amusing that people really do that though.
- sambe 7y agoSome people switch programming languages often. Exponentiation operators are common, both caret and double star are used. It does not seem amusing (surprising?) to me. On the other hand, piping and redirection are relatively uncommon outside of the shell. There is certainly a clearer mental distinction for me between "I am in shell" and "I am not in shell" than "I should double star".
- oytis 7y agoC doesn't have double star either (in fact double star means double dereferencing). YMMV or course, but distinction between a low-level language like C, and a higher-level one that can support exponentiation, matrix multiplication, thread spawning etc. as a first-class language construct is as clear to me as the difference between shell and non-shell.
- aidenn0 7y agoSomeone correct me if I'm wrong, but I believe double star as an infix operator would be interpreted as multiplication of a dereference: X ** Y would be parsed as X * (*Y)
- deathanatos 7y agoI recently found several instances of hand-crafted for loops in our Python code with: j = 0 while condition(): ++j In Python, there is no "increment" operator, so `++j` is parsed as two unary plus operators: it's a no-op.
- dagw 7y agoI recently found the following bug in some python code at work x = 12 //10 What they where apparently trying to do was to change the value of x while commenting out the old value for reference. Except in python // is integer division and not comments so x was set to 1 instead of 12.
- alexeiz 7y agoThis is silly. One of my personal stupid mistakes in Python is forgetting to delete a trailing comma. The resulting code is still valid, because a trailing comma produces a tuple. a = b,
- dagw 7y agoOr accidentally forgetting a comma like in a = ["long", "list" "of", "strings] which become a = ["long", "listof", "strings"] due to automatic string concatenation
- guitarbill 7y agothe bugbear plugin for flake8 can warn about this: https://pypi.org/project/flake8-bugbear/ https://pypi.org/project/flake8-bugbear/
- cryptonector 7y agoFrom the HN submission title it wasn't obvious that the issue is people misunderstanding that the ^ operator isn't the exponential operator but the XOR operator. Clicking through made it clear. Warning about constant subexpressions where the LHS and RHS of ^ are constant literals makes a lot of sense to me, but if the LHS is a constant expression but not a constant literal, then not so much. Also, warning about this will cause some false positives where macro expansions are involved -- probably not a good thing.
- typopl 7y agoMy perl script to scan C/C++ source code for common programming typos can check for this particular case since 1999. see https://www.cpan.org/authors/id/T/TY/TYPO/typo-2.46.pl https://www.cpan.org/authors/id/T/TY/TYPO/typo-2.46.pl // START: Mon Jun 17 14:49:53 2019 C:\src\test\test.c (1): using ^ on a number 37: short#maxshort=2^16; C:\src\test\test.c (2): using ^ on a number 37: int#maxint=2^32; C:\src\test\test.c (3): using ^ on a number 37: long#maxlong=2^64; Link to the 1999 Perl Conference paper for the script: https://web.archive.org/web/20090729104734/http://www.geocities.com/typopl/ https://web.archive.org/web/20090729104734/http://www.geocit...
- deleted 7y ago[deleted]
- deleted 7y ago[deleted]
- kazinator 7y agoGCC should understand the expression grammars of no fewer than a dozen other languages and warn you when something could syntactically be an expression from another language, but with a different meaning. GCC should also change your diaper, burp you, and issue random diagnostics like foo.c:32: warning: did you test this yet?
- kazinator 7y agoGCC has gotten too bloated. It could benefit from compile time CONFIG_ options to remove all the fluff, such as superfluous diagnostics. If for whatever reason I have to include GCC in an embedded this sort of "misleading indentation" and "suggest parentheses" nonsense just wastes precious flash space.