Here is a g++ error message that took me an embarrassingly long time to figure out:
In file included from /home/llvm-project/llvm/lib/IR/Constants.cpp:15: /home/llvm-project/llvm/lib/IR/LLVMContextImpl.h:447:11: error: explicit specialization in non-namespace scope ‘struct llvm::MDNodeKeyImpl<llvm::DIBasicType>’ template <> struct MDNodeKeyImpl<DIStringType> { ^
This is the code:
template <> struct MDNodeKeyImpl<DIStringType> { unsigned Tag; MDString *Name; Metadata *StringLength; Metadata *StringLengthExp; Metadata *StringLocationExp; uint64_t SizeInBits; uint32_t AlignInBits; unsigned Encoding;
This specialization isn’t materially different than the one that preceded it:
template <> struct MDNodeKeyImpl<DIBasicType> { unsigned Tag; MDString *Name; MDString *PictureString; uint64_t SizeInBits; uint32_t AlignInBits; unsigned Encoding; unsigned Flags; Optional<DIBasicType::DecimalInfo> DecimalAttrInfo; MDNodeKeyImpl(unsigned Tag, MDString *Name, MDString *PictureString, uint64_t SizeInBits, uint32_t AlignInBits, unsigned Encoding, unsigned Flags, Optional<DIBasicType::DecimalInfo> DecimalAttrInfo) : Tag(Tag), Name(Name), PictureString(PictureString), SizeInBits(SizeInBits), AlignInBits(AlignInBits), Encoding(Encoding), Flags(Flags), DecimalAttrInfo(DecimalAttrInfo) {} MDNodeKeyImpl(const DIBasicType *N) : Tag(N->getTag()), Name(N->getRawName()), PictureString(N->getRawPictureString()), SizeInBits(N->getSizeInBits()), AlignInBits(N->getAlignInBits()), Encoding(N->getEncoding()), Flags(N->getFlags(), DecimalAttrInfo(N->getDecimalInfo()) {} bool isKeyOf(const DIBasicType *RHS) const { return Tag == RHS->getTag() && Name == RHS->getRawName() && PictureString == RHS->getRawPictureString() && SizeInBits == RHS->getSizeInBits() && AlignInBits == RHS->getAlignInBits() && Encoding == RHS->getEncoding() && Flags == RHS->getFlags() && DecimalAttrInfo == RHS->getDecimalInfo(); } unsigned getHashValue() const { return hash_combine(Tag, Name, SizeInBits, AlignInBits, Encoding); } };
However, there is an error hiding above it on this line:
i.e. a single missing brace in the initializer for the Flags member, a consequence of a cut and paste during rebase that clobbered that one character, when adding a comma after it.
It turns out that the compiler was giving me a hint that something was wrong before this in the message:
as it states that the scope is:
which is the previous class definition. Inspection of the code made me think that the scope was ‘namespace llvm {…}’, and I’d gone looking for a rebase error that would have incorrectly terminated that llvm namespace scope. This is a classic example of not paying enough attention to what is in front of you, and going off looking based on hunches instead. I didn’t understand the compiler message, but in retrospect, non-namespace scope meant that something in that scope was incomplete. The compiler wasn’t smart enough to tell me that the previous specialization was completed due to the missing brace, but it did tell me that something was wrong in that previous specialization (which was explicitly named), and I didn’t look at that because of my “what the hell does that mean” reaction to the compilation error message.
In this case, I was building on RHEL8.3, which uses an ancient GCC toolchain. I wonder if newer versions of g++ fare better (i.e.: a message like “possibly unterminated brace on line …” would have been much nicer)? I wasn’t able to try with clang++ as I was building llvm+clang+lldb (V14), and had uninstalled all of the llvm related toolchain to avoid interference.
As your T.A., I have to punish you …
December 19, 2020 C/C++ development and debugging. grading comments, horrible code, macros, token pasting
Back in university, I had to implement a reverse polish notation calculator in a software engineering class. Overall the assignment was pretty stupid, and I entertained myself by generating writing a very compact implementation. It worked perfectly, but I got a 25/40 (62.5%) grade on it. That mark was well deserved, although I did not think so at the time.
The grading remarks were actually some of best feedback that I ever received, and also really funny to boot. I don’t know the name of this old now-nameless TA anymore, but I took his advice to heart, and kept his grading remarks on my wall in my IBM office for years. That served as an excellent reminder not to write over complicated code.
Today, I found those remarks again, and am posting them for posterity. Enjoy!
Transcription for easy reading
Reflection.
The only part of this feedback that I would refute was the comment about the string class. That was a actually a pretty good string implementation. I didn’t write it because I was a viscous mouse hunter, but because I hit a porting issue with pre-std:: C. In particular, we had two sets of Solaris machines available to us, and I was using one that had a compiler that included a nice C++ string class. So, naturally I used it. For submission, our code had to compile an run on a different Solaris machine, and lo and behold, the string class that all my code was based on was not available.
What I should have done (20/20 hindsight), was throw out my horrendous code, and start over from scratch. However, I took the more fun approach, and wrote my own string class so that my machine would compile on either machine.
Amusingly, when I worked on IBM LUW, there was a part of the query optimizer code seemed to have learned all it’s tricks from the ugly macros and token pasting that I did in this assignment. It was truly gross, but there was 10000x more of it than my assignment. Having been thoroughly punished for my atrocities, I easily recognized this code for the evil it was. The only way that you could debug that optimizer code, was by running it through the preprocessor, cut and pasting the results, and filtering that cut and paste through something like cindent (these days you would probably use clang-format.) That code was brutal, and I always wished that it’s authors had had the good luck of having a TA like mine. That code is probably still part of LUW terrorizing developers. Apparently the justification for it was that it was originally written by an IBM researcher using templates, but templates couldn’t be used in DB2 code because we didn’t have compiler on all platforms that supported them at the time.
I have used token pasting macros very judiciously and sparingly in the 26 years since I originally used them in this assignment, and I do think that there are a few good uses for that sort of generative code. However, if you do have to write that sort of code, I think it’s better to write perl (or some other language) code that generates understandable code that can be debugged, instead of relying on token pasting.
Share this:
Like this: