The setup (source files are listed at the bottom). There is a Counters class with 2 counter members and 2 methods to increment them and a 3rd extra #ifndef NDEBUG-guarded "debug" counter and its incrementer. These incrementer methods are provided through additional functions defined in their respective headers.
The bug. This is a contrived scenario, but the "-DNDEBUG" compilation flag is provided for only one of the two headers. The effect of this is that the wrong counter is incremented in the final program: main.cpp prints:
$ CFLAGS='-DNDEBUG' make; ./main
> Counter 1: 0
> Counter 2: 4
> Debug all counters: 7
main.cpp:
#include<iostream>
#include "counters.hpp"
#include "increment1.hpp"
#include "increment2.hpp"
using namespace std;
int main(int argc, char *argv[]){
Counters c;
increment1(c);increment1(c);increment1(c);
increment2(c);increment2(c);increment2(c);increment2(c);
cout << "Counter 1: " << c.getCounter1() << endl;
cout << "Counter 2: " << c.getCounter2() << endl;
#ifndef NDEBUG
cout << "Debug all counters: " << c.getDebugAllCounters() << endl;
#endif
return 0;}
Purportedly, this is because when increment1.cpp receives the "no-debug" version of the Counters class and is therefore unaware of the extra debug counter, which is now the 1st member of the struct, and erroneously increments it instead of m_counter. (This is easily checked by moving the the debug counter below the two main counters)
I'm imagining that this is because clang++ -c -DNDEBUG -O2 increment1.cpp builds increment1.cpp (see Makefile below) with a model of Counters which only has two (instead of three) classes, but in the final main.cpp as well as increment2.cpp there are three memebers. So
// increment1.cpp
#include "counters.hpp"
void increment1(Counters& counters){counters.inc1();}
actually turns into pseudocode
// .. 
// .. whole non-debug version of Counters
// ..
void increment1(Counters& counters){
       [the Counters instance]->[first_member]++;}
}
Q1
How does this inlining of the class method into a function call take place? Does the differing names of members not matter in this case because inlining takes place at the register level? Is there a compiler flag (like -S) I can use to see this intermediate inlined representation?
Q2
This error doesn't go away even if i replace all -O2's with -O0's in the Makefile or add __attribute__((optnone)) to inc1. If inlining is the culprit, what am i missing?
Counters.hpp
increment1.cpp
increment1.hpp
increment2.cpp
increment2.hpp
main.cpp
Counters.hpp
class Counters
{
private:
    #ifndef NDEBUG   
        int m_debugAllCounters;  
    #endif
    int m_counter1;
    int m_counter2;
public:
    Counters() :
    #ifndef NDEBUG 
        m_debugAllCounters(0),
    #endif
                 m_counter1(0), m_counter2(0){}
     void inc1(){
        #ifndef NDEBUG  
            m_debugAllCounters++;
        #endif
        m_counter1++;}
     void inc2(){
        #ifndef NDEBUG  
            m_debugAllCounters++;
        #endif
        m_counter2++; }
    int getCounter1() const { return m_counter1; }
    int getCounter2() const { return m_counter2; }
    #ifndef NDEBUG
    int getDebugAllCounters() const { return m_debugAllCounters; }
    #endif
};
increment1.hpp
class Counters;
int increment1(Counters&);
increment1.cpp
#include "counters.hpp"
void increment1(Counters& counters){counters.inc1();}
increment2.hpp
class Counters;
int increment2(Counters&);
increment2.cpp
#include "counters.hpp"
void increment2(Counters& counters){counters.inc2();}
Makefile
all: main.o increment1.o increment2.o
    clang++ -o main main.o increment1.o increment2.o
main.o: main.cpp increment1.hpp increment2.hpp counters.hpp
    clang++ -c -O2 main.cpp
increment1.o: increment1.cpp counters.hpp
    clang++ -c $(CFLAGS) -O2 increment1.cpp
increment2.o: increment2.cpp counters.hpp
    clang++ -c -O2 increment2.cpp
clean:
    rm -f *.o diff-flags
This bug is outlined in the following article("Compiling with different flags" section at the bottom).
