2

Let's analyze the following code example:

int main() {
    return func();
}

int func() {
    return 1;
}

Clang will report error:

/private/tmp/src.cpp:2,9 - Error - use of undeclared identifier 'func'

But i can find lot's of such errors in wide used examples, f.e. some arduino sources: https://github.com/firmata/arduino/blob/master/examples/StandardFirmata/StandardFirmata.ino

line 235 : disableI2CPins(); // invocation (before definition, no declaration before)

...

line 651 : void disableI2CPins() { .. } // definition

Though function is used before defined that code can be compiled with GCC successfully. How that can be? Is it different Clang and GCC behavior? Are there any flags to allow this for Clang?

PS. Here is successful command line to compile the code used by Arduino IDE:

/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10600 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -I/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/cores/arduino -I/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/variants/mega -I/Applications/Arduino.app/Contents/Resources/Java/libraries/Servo/src -I/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/libraries/Wire -I/Applications/Arduino.app/Contents/Resources/Java/libraries/Firmata/src /var/folders/64/fwfkm1k51zbd4_c5lwpsbljh0000gn/T/build8049651108713091801.tmp/StandardFirmata.cpp -o /var/folders/64/fwfkm1k51zbd4_c5lwpsbljh0000gn/T/build8049651108713091801.tmp/StandardFirmata.cpp.o

4ntoine
  • 19,816
  • 21
  • 96
  • 220
  • There are flags for both that can affect how "picky" the compilers are. IANALL, but I believe forwards definitions of this type are not allowed in (later) C++ specs (hence the error from Clang) but were very common in possibly older C++; definitely old C (such a function was assumed to return int). Because there's lots of old code that was written this way, I'm guessing gcc chose to allow them (though I'd be slightly surprised if it didn't issue a warning). – TripeHound Nov 10 '15 at 12:09
  • what are the flags to make Clang work like GCC in this case? – 4ntoine Nov 10 '15 at 12:13
  • 1
    I don't know: I've never used it. However, [this page on Clang compatibility](http://clang.llvm.org/compatibility.html) says "_Clang is more strict than other popular compilers, and may reject incorrect code that other compilers allow_" so there may not be an option for what you want. You may have to fix the code by using prototypes. – TripeHound Nov 10 '15 at 12:22

3 Answers3

4

This is actually not to do with the compiler, but the Arduino IDE. The IDE has a preprocessing step which forward-declares all the defined functions so that you don't need to worry about function placement in the file.

My strong recommendation would be to just follow the language rules and forward-declare your functions or define them before their use. Your code will be more portable and understandable for it.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • i know Arduino IDE adds `#include ` in the very beginning of the files but in that case it does not matter. This works for libraries, but in the example above does not have header. So it's GCC who does not care about this and not Arduino IDE who fixes it behind the scene. – 4ntoine Nov 10 '15 at 12:26
  • 1
    @4ntoine: it's not just `#include `, the Arduino IDE *also* adds prototypes for all functions defined in the sketch at the top of the file (often making a bloody mess if you have function which take a `struct` as a parameter). – Matteo Italia Nov 10 '15 at 12:45
  • 1
    @4ntoine: see [here](http://ideone.com/3bqpJm): "plain" g++ refuses to compile your example too. It's the Arduino IDE preprocessing crap that allows for this. – Matteo Italia Nov 10 '15 at 12:47
  • oh, you're right! I should look .cpp file to make sure and i've found Arduino IDE adds functions declarations after including Arduino.h: #include "Arduino.h" ... void enableI2CPins(); void disableI2CPins(); ... void setup(); void loop(); #line 54 This is the correct answer, thanks – 4ntoine Nov 10 '15 at 12:48
  • wow, i've found a bug here! If i introduce any Class in sketch and it's passes in function argument, Arduino IDE adds function declaration in the beginning of the file, but Class is declared below it. It leads to compilation error ".. was not declared".. Just checked it in latest Arduino IDE. – 4ntoine Nov 11 '15 at 12:20
1

No, you don't want clang to behave like gcc in this regard. In fact, use -Wall and -W (or -Wextra) in order to get gcc and clang both to give you even more warnings.

But i can find lot's of such errors in wide used examples

Sadly, getting C and C++ right is so hard that widely used examples are not good enough.

Your example includes a bug that arguably has no real effect on this program. EDIT: it appears that the IDE is clever enough to workaround language features. Refer to @TartanLlama's answer for details. Perhaps the author could make the claim that it's by design. But it's so rarely the case that clang thankfully saves you from yourself.

C89 §3.3.2.2, "Function calls" includes:

If the expression that precedes the parenthesized argument list in a function call consists solely of an identifier, and if no declaration is visible for this identifier, the identifier is implicitly declared exactly as if, in the innermost block containing the function call, the declaration

extern int  identifier();

appeared

When I first learned that, I thought to myself, "OMG that's not what I wanted at all!" And, so it went with so many other folks that this "feature" was changed in C99.

Brian Cain
  • 14,403
  • 3
  • 50
  • 88
  • actually i do want clang work as GCC since i'm using Clang as static analyzer in my IDE-like app. – 4ntoine Nov 10 '15 at 12:28
  • Great, you found a bug using `clang` as a static analysis tool! – Brian Cain Nov 10 '15 at 12:29
  • It seems like GCC bug most likely – 4ntoine Nov 10 '15 at 12:31
  • You could make that case but you might have a hard time getting that team to accept it as a bug. Mostly this is due to different default settings between the compilers. – Brian Cain Nov 10 '15 at 12:32
  • my intent is not to report it to GCC (i believe it's made for compatibility reason and it's not a bug actually) but to make Clang analyze it like GCC – 4ntoine Nov 10 '15 at 12:35
0

Clang is per default in GNU C11 mode which is uses C99 semantics. In C99 the implicit function declaration is not allowed anymore.

you can put clang into c89 mode with clang --std=c89 or clang --std=gnu89 which should allow that behavior,

See: Are prototypes required for all functions in C89, C90 or C99?

Martin Bonetti
  • 452
  • 4
  • 9
  • MBA-Anton:tmp asmirnov$ clang ./src.cpp -o ./src --std=c89 error: invalid argument '--std=c89' not allowed with 'C++/ObjC++' MBA-Anton:tmp asmirnov$ clang ./src.cpp -o ./src --std=gnu89 error: invalid argument '--std=gnu89' not allowed with 'C++/ObjC++' – 4ntoine Nov 10 '15 at 12:44
  • 1
    you are using a cpp file. The c modes are only allowed if you are compiling c code. C++ does not allow implicit function declarations – Martin Bonetti Nov 10 '15 at 13:17
  • 1
    that's because the question was about c++ but you answered for c for some reason. sorry i was not clear enough – 4ntoine Nov 10 '15 at 14:43