The packing size used for class template instantiation is set by the #pragma pack directive which is in effect at the point of template instantiation (whether implicit or explicit), rather then by the #pragma pack directive which was in effect at the point of class template definition. This is not a correct behaviour, because in a complex program it's hard to control the point of implicit instantiation of any given template class. And the wrong packing size is leading to incorrect code generation if a given template class was instantiadet with different packing sizes in different translation units. Consider the code examples in Howtorepeat section. Related problem reports: other/4957 Release: 3.2 (and seen on mainline -LJR) Environment: System: FreeBSD gate.studio1001.akella.ru 4.4-RELEASE FreeBSD 4.4-RELEASE #11: Thu Jun 6 12:58:54 host: i386-unknown-freebsd4.4 build: i386-unknown-freebsd4.4 target: i386-unknown-freebsd4.4 configured with: /usr/src/gcc-3.2/configure --prefix=/usr How-To-Repeat: 1. Try to compile and run the following example (all code examples in this report could be compiled with "g++ file.cpp", no special command-line options are required) //---------------------------- begin example #include <stdio.h> #pragma pack(4) template <typename X > struct T { char x1; /* Usually 3 padding bytes are added after x1 member. */ int x2; }; #pragma pack(1) template T<int>; /* T<int> is instantiated here */ #pragma pack(4) template T<float>; /* T<float> is instantiated here */ int main() { printf("sizeof T<int> = %d\n", sizeof(T<int>)); printf("sizeof T<float> = %d\n", sizeof(T<float>)); } //---------------------------- end example (parameter types 'int' and 'float' are not used in any way, they are required just to separate two template instances) The output is: sizeof T<int> = 5 sizeof T<float> = 8 Which shows that the size (and member offsets) of the template class depends on the #pragma pack at place of instantiation. 2. There is a more complicated example which shows how to trigger this bug accidently. Unpack the attached archive and compile two sample translation units (tu01.cpp and tu02.cpp) in alignment_bug/case_02 directory. The only difference between these translation units is the order of #include directives which causes different implicit instantiation of the same template class (include guards are skipped in such simple case). This test case repeats the behaviour reported in other/4957.
Fix: Do not rely on implicit instantiation. Instantiate all desired template classes explicitly (sic!). LJR: Also consider using the attribute form instead of pragma form to control implicit instantiation behavior. This binds closer. template <typename X > struct T { char x1; /* Usually 3 padding bytes are added after x1 member. */ int x2; } __attribute ((__packed__));
State-Changed-From-To: open->analyzed State-Changed-Why: Igor created a small test case which displays the behavior he considers a bug and included an analysis of the failure. I will add only one comment: because a feature is hard to use in a complex program does not imply that the feature has incorrect behaviour. In this case, #pragma pack came from a C ABI. It is doubtful anyone ever looked at the rules as applied to C ++. E.g. no documentation on such interactions is found in tm.texi. One correct resolution to this PR is to update our documentation. (I will also add another user-level fix.)
From: "Igor A. Goussarov" <igusarov@akella.com> To: ljrittle@gcc.gnu.org, gcc-bugs@gcc.gnu.org, gcc-prs@gcc.gnu.org, nobody@gcc.gnu.org, gcc-gnats@gcc.gnu.org Cc: Subject: Re: c++/10332: Template classes are not instantiated correctly in presense of #pragma pack() Date: Fri, 11 Apr 2003 15:46:35 +0400 Hello Loren and the team, Thank you for taking time to examine this another report! Unfortunately the fix you suggested isn't very good for two reasons: a) __atribute__ is much less portable (compared to #pragma pack which is supported at least by gcc, MSVC, BCB and CodeWarrior) b) the fix doesn't address the problem. I _don't_ want to have packed template classes. I want to have ordinary templates, which use default packing and alignment. But: if this template occasiously got instantiated at the point where "#pragma pack" was set to some obscure value, then the template instance somewhy become packed! This is the behaviour I object to. Let me put it in other words: 1. I wrote a template class which doesn't need any packing. 2. I was using this template class in many translation units all over the program. 3. One day I wrote another not-template class (or POD struct), which _has_ to be packed. 4. If the template class in question is implicitly instantiated from within the said packed struct, it miraculously becomes packed. 5. More disasters follows when several TUs are linked together... So you see, I'm not trying to apply packing to template classes intentionally. Well, in a sense, packing and templates are used together, since a single translation unit contains them both. But it doesn't look right to me that some class affects the instantiation of a previously defined template. This is kind of... not right. For example, imagine that the layout of vtable for polymorphic classes was dependent on the use of some feature in some other place in the program. This would come as a surprise to the developer of the said class, because he always firmly belived that since the class was fully defined nothing more can change it. So did I, and so was I surprised. Actually, I've called this behaviour "a bug" because I get used to think that once a class was defined nothing more can change it (and inherently its layout). Being a programmer myself I can see where all this comes from: it is likely that gcc uses some global variable for storing the current packing size instead of associating the packing size with each individual data structure. Thus, when there is a need to create an instance of a template class, the compiler simply uses the current value of that global variable. If this is the case, then changing such behaviour of the compiler is not easy. I mean, it looks more like design issue rather then like implementation bug. Also I can see that such situation is impossible to detect and thus no diagnostic message is ever possible... Pity. Best Regards, Igor
From: Momchil Velikov <velco@fadata.bg> To: "Igor A. Goussarov" <igusarov@akella.com> Cc: ljrittle@gcc.gnu.org, gcc-bugs@gcc.gnu.org, gcc-prs@gcc.gnu.org, nobody@gcc.gnu.org, gcc-gnats@gcc.gnu.org Subject: Re: c++/10332: Template classes are not instantiated correctly in presense of #pragma pack() Date: 11 Apr 2003 16:52:55 +0300 >>>>> "Igor" == Igor A Goussarov <igusarov@akella.com> writes: Igor> Being a programmer myself I can see where all this comes from: it Igor> is likely that gcc uses some global variable for storing the current Igor> packing size instead of associating the packing size with each Igor> individual data structure. Thus, when there is a need to create an Igor> instance of a template class, the compiler simply uses the current Igor> value of that global variable. What is the scope of #pragma pack ? ~velco
From: Momchil Velikov <velco@fadata.bg> To: "Igor A. Goussarov" <igusarov@akella.com> Cc: ljrittle@gcc.gnu.org, gcc-bugs@gcc.gnu.org, gcc-prs@gcc.gnu.org, nobody@gcc.gnu.org, gcc-gnats@gcc.gnu.org Subject: Re: c++/10332: Template classes are not instantiated correctly in presense of #pragma pack() Date: 14 Apr 2003 11:57:04 +0300 >>>>> "Igor" == Igor A Goussarov <igusarov@akella.com> writes: Igor> Momchil Velikov wrote: >> What is the scope of #pragma pack ? Igor> Apparently, from the line it is encountered at till the end of Igor> translation unit or till the next #pragma pack. In that case holding it in a global variable looks appropriate, no ? ~velco
From: "Igor A. Goussarov" <igusarov@akella.com> To: Momchil Velikov <velco@fadata.bg> Cc: ljrittle@gcc.gnu.org, gcc-bugs@gcc.gnu.org, gcc-prs@gcc.gnu.org, nobody@gcc.gnu.org, gcc-gnats@gcc.gnu.org Subject: Re: c++/10332: Template classes are not instantiated correctly in presense of #pragma pack() Date: Mon, 14 Apr 2003 12:50:28 +0400 Momchil Velikov wrote: > What is the scope of #pragma pack ? Apparently, from the line it is encountered at till the end of translation unit or till the next #pragma pack. Er... Was it one of them trick questions? Best Regards, Igor
From: "Igor A. Goussarov" <igusarov@akella.com> To: Momchil Velikov <velco@fadata.bg> Cc: ljrittle@gcc.gnu.org, gcc-bugs@gcc.gnu.org, gcc-prs@gcc.gnu.org, nobody@gcc.gnu.org, gcc-gnats@gcc.gnu.org Subject: Re: c++/10332: Template classes are not instantiated correctly in presense of #pragma pack() Date: Mon, 14 Apr 2003 14:02:26 +0400 Momchil Velikov wrote: >>>>>>"Igor" == Igor A Goussarov <igusarov@akella.com> writes: > Igor> Momchil Velikov wrote: > >> What is the scope of #pragma pack ? > > Igor> Apparently, from the line it is encountered at till the end of > Igor> translation unit or till the next #pragma pack. > > In that case holding it in a global variable looks appropriate, no ? Up to a certain degree, yes. The concept of "packing" is applied to structures, thus the packing size is a property of each structure. Holding it in a global variable is justifiable only if there's absolutely no chance that this variable could potentially be altered before it is used to create an internal compiler representation of the structure in question. I grant that C plain structures are "instantiated" immediately at the point of their definition. C++ templates are not. The compiler still have to store the last packing size in a global var (or a stack, to support #pragma push/pop). But as soon as a struct definition is encountered (whether a template or not), the value of that global var should've been copied to that structure description. And the compiler should later use the value from the structure description rather then from the global var. In other words, the global var only stores the current packing size and is used _solely_ to initialize the packing size of each new defined struct. As Loren has mentioned, #pragma pack came from C compiler, and probably its interference with C++ entities was not fully considered... By now, I'm most interested in figuring out the point of view of the gcc developers: do you tend to think that the current interference of #pragma pack and templates is an undocumented feature or an incorrect behaviour? Best Regards, Igor
From: Loren James Rittle <rittle@latour.rsch.comm.mot.com> To: igusarov@akella.com Cc: velco@fadata.bg, ljrittle@gcc.gnu.org, gcc-bugs@gcc.gnu.org, gcc-prs@gcc.gnu.org, nobody@gcc.gnu.org, gcc-gnats@gcc.gnu.org Subject: Re: c++/10332: Template classes are not instantiated correctly in presense of #pragma pack() Date: Mon, 14 Apr 2003 18:42:04 -0500 (CDT) In article <3E9A8732.7000600@akella.com>, "Igor A. Goussarov" <igusarov@akella.com> writes: >>>> What is the scope of #pragma pack ? Igor> Apparently, from the line it is encountered at till the end of Igor> translation unit or till the next #pragma pack. >> In that case holding it in a global variable looks appropriate, no ? > Up to a certain degree, yes. The concept of "packing" is applied to > structures, thus the packing size is a property of each structure. > Holding it in a global variable is justifiable only if there's > absolutely no chance that this variable could potentially be altered > before it is used to create an internal compiler representation of the > structure in question. I grant that C plain structures are > "instantiated" immediately at the point of their definition. C++ > templates are not. [...] > By now, I'm most interested in figuring out the point of view of the > gcc developers: do you tend to think that the current interference of > #pragma pack and templates is an undocumented feature or an incorrect > behaviour? Agreed. The proper fix is to either (a) document what is happening, only available if no external spec says we are doing something wrong, or (b) retain the global flag but capture it's value and bind it for use when the C++ template is actually instantiated. I'd favor (b) unless disallowed by the spec(s) that we claim to implement for this pragma. This is either an easy patch (if the infrastructure allows it) or a nightmare (if it doesn't). FYI, if updating the documentation is not enough, this is outside my area to productively fix. Regards, Loren
From: "Igor A. Goussarov" <igusarov@akella.com> To: rittle@labs.mot.com Cc: velco@fadata.bg, ljrittle@gcc.gnu.org, gcc-bugs@gcc.gnu.org, gcc-prs@gcc.gnu.org, nobody@gcc.gnu.org, gcc-gnats@gcc.gnu.org Subject: Re: c++/10332: Template classes are not instantiated correctly in presense of #pragma pack() Date: Tue, 15 Apr 2003 17:39:08 +0400 Loren James Rittle wrote: > The proper fix is to either (a) document what is happening, > only available if no external spec says we are doing something wrong, > or (b) retain the global flag but capture it's value and bind it for > use when the C++ template is actually instantiated. The whole realm of #pragma is out of scope of the C++ standard. And I don't know which other documents could be considered relyable external specs... I can suggest examining the behaviour of other compilers (to keep the behaviour consistent) or taking this question of the correct behaviour to the trusted expert or community. Best Regards, Igor
This is a dup of the older bug 7046 which describes the problem better and does not have too many comments. *** This bug has been marked as a duplicate of 7046 ***