LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 6353 - clang emits U symbol while gcc emits W one
Summary: clang emits U symbol while gcc emits W one
Status: RESOLVED FIXED
Alias: None
Product: clang
Classification: Unclassified
Component: C++ (show other bugs)
Version: unspecified
Hardware: PC Linux
: P normal
Assignee: Unassigned Clang Bugs
URL:
Keywords:
Depends on:
Blocks: 5881
  Show dependency tree
 
Reported: 2010-02-19 08:53 PST by Roman Divacky
Modified: 2010-03-06 14:07 PST (History)
8 users (show)

See Also:
Fixed By Commit(s):


Attachments
test case (4.36 KB, text/x-c++src)
2010-02-19 08:53 PST, Roman Divacky
Details
test case (3.02 KB, text/x-c++src)
2010-02-21 15:10 PST, Roman Divacky
Details
testcase (328 bytes, text/x-c++src)
2010-02-21 16:19 PST, Rafael Ávila de Espíndola
Details
add asserts to the codegen (1.08 KB, patch)
2010-02-22 11:17 PST, Rafael Ávila de Espíndola
Details
testcase. With the asserts in place the testcase can be a bit smaller. (252 bytes, text/x-c++src)
2010-02-22 11:18 PST, Rafael Ávila de Espíndola
Details
patch (1.86 KB, patch)
2010-02-23 23:43 PST, Rafael Ávila de Espíndola
Details
patch (6.26 KB, patch)
2010-02-27 15:56 PST, Rafael Ávila de Espíndola
Details
patch (7.32 KB, patch)
2010-02-27 18:03 PST, Rafael Ávila de Espíndola
Details
patch (8.10 KB, patch)
2010-02-27 22:12 PST, Rafael Ávila de Espíndola
Details
patch (10.23 KB, patch)
2010-02-28 11:41 PST, Rafael Ávila de Espíndola
Details
patch (9.21 KB, patch)
2010-03-01 22:50 PST, Rafael Ávila de Espíndola
Details
patch (9.47 KB, patch)
2010-03-02 12:51 PST, Rafael Ávila de Espíndola
Details
patch (9.49 KB, patch)
2010-03-02 13:31 PST, Rafael Ávila de Espíndola
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Roman Divacky 2010-02-19 08:53:28 PST
when compiling the attached test case I am getting:

pes delta$ clang++ -O0 -c foo.cpp && nm foo.o | grep Rb_tree

                 U _ZN8_Rb_treeIPK4Decl4pairIKS2_9CharUnitsE10_Select1stIS6_E4lessIS2_E9allocatorIS6_EED1Ev

pes delta$ g++ -O0 -c foo.cpp && nm foo.o | grep Rb_tree
0000000000000000 W _ZN8_Rb_treeIPK4Decl4pairIKS2_9CharUnitsE10_Select1stIS6_E4lessIS2_E9allocatorIS6_EED1Ev
Comment 1 Roman Divacky 2010-02-19 08:53:54 PST
Created attachment 4256 [details]
test case
Comment 2 Roman Divacky 2010-02-21 15:10:23 PST
Created attachment 4268 [details]
test case

a little more reduced test case...
Comment 3 Rafael Ávila de Espíndola 2010-02-21 16:19:16 PST
Created attachment 4272 [details]
testcase

reduced it a bit more
Comment 4 Rafael Ávila de Espíndola 2010-02-21 17:27:46 PST
looks like the problem is that ~_Rb_tree() is {Ptr = 0} when CodeGenModule::GetOrCreateLLVMFunction is deciding if it should be emitted. As a consequence FD->isThisDeclarationADefinition() returns false and the destructor is not emitted.

This is true even if there is some real code in ~_Rb_tree().
Comment 5 Rafael Ávila de Espíndola 2010-02-21 18:05:18 PST
Looking a bit more it looks like the problem is with the template instantiation. Sema::ActOnFinishFunctionBody is being called on the template of the destructor, but never on the instantiated one. That is why setBody is never called.
Comment 6 Rafael Ávila de Espíndola 2010-02-21 18:51:32 PST
Going one level up, the issue is that Sema::MarkDeclarationReferenced is never called on ~_Rb_tree
Comment 7 Rafael Ávila de Espíndola 2010-02-21 19:35:43 PST
OK, looks like this needs bigger changes to clang than I can get done tonight. If anyone else is interested in fixing this bug, go for it. I might try again next weekend.

This is what is happening:

We have special code for handing emitting implicit destructors in CodeGenModule::GetOrCreateLLVMFunction. The problem happens when a implicit destructor references an explicit one that is defined in a template. In the testcase the implicit destructor is ~BlockFunction() and the explicit one is ~_Rb_tree().

The problem is that since the special case is on codegen, that is too late to call Sema::MarkDeclarationReferenced. Normally this method is the one that would add the destructor to the PendingImplicitInstantiations list. Being on that list eventually causes InstantiateFunctionDefinition to be called on it and that finally calls setBody.

This is not a problem for non-templates since the body is set early on (no instantiation is required).

Looks like what we need is to replicated the existing logic from CodeGen into Sema. Template instantiation is a form of code gen anyway :-)

Using the attached test as an example, the calls that have to happen are:

* ActOnFinishFunctionBody on the implicit ~BlockFunction
* MarkBaseAndMemberDestructorsReferenced on ~BlockFunction
* MarkDeclarationReferenced on ~_Rb_tree()

Another option is to add a ActOnImplicitConstructorOrDestructor. I don't have enough experience on clang to know which one is better.
Comment 8 Douglas Gregor 2010-02-22 10:40:55 PST
(In reply to comment #7)
> OK, looks like this needs bigger changes to clang than I can get done tonight.
> If anyone else is interested in fixing this bug, go for it. I might try again
> next weekend.
> 
> This is what is happening:
> 
> We have special code for handing emitting implicit destructors in
> CodeGenModule::GetOrCreateLLVMFunction. The problem happens when a implicit
> destructor references an explicit one that is defined in a template. In the
> testcase the implicit destructor is ~BlockFunction() and the explicit one is
> ~_Rb_tree().

This seems like a good place to assert() that Sema did its job and marked the implicit destructor as being used.

> The problem is that since the special case is on codegen, that is too late to
> call Sema::MarkDeclarationReferenced. Normally this method is the one that
> would add the destructor to the PendingImplicitInstantiations list. Being on
> that list eventually causes InstantiateFunctionDefinition to be called on it
> and that finally calls setBody.

Right.

> This is not a problem for non-templates since the body is set early on (no
> instantiation is required).
> 
> Looks like what we need is to replicated the existing logic from CodeGen into
> Sema. Template instantiation is a form of code gen anyway :-)

Much of this logic already exists in Sema; we're probably just missing a MarkDeclarationReferenced call where we're implicitly using a destructor.

  - Doug
Comment 9 Rafael Ávila de Espíndola 2010-02-22 11:17:23 PST
Created attachment 4284 [details]
add asserts to the codegen
Comment 10 Rafael Ávila de Espíndola 2010-02-22 11:18:12 PST
Created attachment 4285 [details]
testcase. With the asserts in place the testcase can be a bit smaller.
Comment 11 Rafael Ávila de Espíndola 2010-02-23 23:43:21 PST
Created attachment 4299 [details]
patch

The attached patch fixes the reduced tested but is incomplete and introduces new failures in the testsuite.
Comment 12 Rafael Ávila de Espíndola 2010-02-24 23:53:43 PST
One testcase that fails the new assert

-------------------------------
struct box {
  virtual ~box();
};

struct pile_box : public box {
  pile_box(box *);
};

pile_box::pile_box(box *pp)
{
}
------------------------------

The issue is that when processing pile_box we just add it to ClassesWithUnmarkedVirtualMembers. We then continue to codegen it, produce the vtable for it and try to codegen ~pile_box() that has not been marked as needed (yet).

Without the assert the code continues to run and eventually ProcessPendingClassesWithUnmarkedVirtualMembers is called and that calls MarkVirtualMembersReferenced which will mark ~pile_box() as used.

Looks like the correct fix is to call MarkVirtualMembersReferenced earlier. What is the reason for delaying it as we do now?
Comment 13 Rafael Ávila de Espíndola 2010-02-27 15:56:33 PST
Created attachment 4330 [details]
patch

Fixes the issue and the "make test" is OK. Will try to bootstrap clang with it.
Comment 14 John McCall 2010-02-27 16:13:26 PST
Overall looks good;  those are great asserts in codegen, thank you.

Please name "needsVtable" "ShouldEmitVtableForCall" or something like that, and have it take a Sema& instead of an ASTContext&, just in case.  Also, conventionally the Sema/Context argument comes first, although we're not terribly consistent about that.
Comment 15 Rafael Ávila de Espíndola 2010-02-27 17:15:15 PST
The following code still fails the new asserts:

class Option {
  virtual ~Option();
};
template <class DataType> class opt : public Option {
  virtual bool handleOccurrence() {
  }
};
template class opt<unsigned>;
Comment 16 Rafael Ávila de Espíndola 2010-02-27 18:03:04 PST
Created attachment 4331 [details]
patch

Fixes all the issues mentioned above. Trying a clang bootstrap again.
Comment 17 Rafael Ávila de Espíndola 2010-02-27 20:31:29 PST
New reduced testcase that fails with the new asserts:

struct allocator     {
  allocator(const allocator&);
};
struct TGError {
  allocator  foo;
  TGError(const allocator &message) :  foo(message) {
  }
};
allocator zed();
TGError foobar() {
  throw TGError(zed());
}
Comment 18 Rafael Ávila de Espíndola 2010-02-27 22:12:16 PST
Created attachment 4332 [details]
patch

Same as before. All know issues fixed, doing a new bootstrap.
Comment 19 Rafael Ávila de Espíndola 2010-02-27 23:31:01 PST
And the new reduced testcase is:

struct TypedInit  {
virtual ~TypedInit();
};
struct OpInit : public TypedInit {
virtual void resolveBitReference();
};
struct BinOpInit : public OpInit {
virtual void getAsString() const;
};
void foobar() {
new BinOpInit();
}
Comment 20 Rafael Ávila de Espíndola 2010-02-28 11:41:32 PST
Created attachment 4335 [details]
patch
Comment 21 Rafael Ávila de Espíndola 2010-03-01 22:50:34 PST
Created attachment 4353 [details]
patch

This one avoids marking a destructor used every time we mask a constructor. Instead it walks the base initializer and marks only those.

The problem with Sema::BuildBaseInitializer is that is is never called in code like

struct TypedInit  {
virtual ~TypedInit();
};
struct OpInit : public TypedInit {
virtual void resolveBitReference();
};
struct BinOpInit : public OpInit {
virtual void getAsString() const;
};
void foobar() {
new BinOpInit();
}
Comment 22 Roman Divacky 2010-03-02 02:59:36 PST
the last patch breaks libstdc++ build with:

/data/home/rdivacky/clangbsd/gnu/lib/libstdc++/../../../contrib/libstdc++/include/ext/stdio_sync_filebuf.h:258:34: error: 
      explicit specialization of 'xsputn' after instantiation
    stdio_sync_filebuf<wchar_t>::xsputn(const wchar_t* __s,

I can provide testcase in the evening
Comment 23 Rafael Ávila de Espíndola 2010-03-02 12:51:07 PST
Created attachment 4360 [details]
patch

This one includes bits from the last review.
"make test" is OK, but I am reducing a clang bootstrap issue with it :-(
Comment 24 Rafael Ávila de Espíndola 2010-03-02 13:15:08 PST
Reduced testcase:

class Option {                                                                                                                                                                                            
virtual ~Option() {                                                                                                                                                                                       
}                                                                                                                                                                                                         
};                                                                                                                                                                                                        
template <class DataType> class opt : public Option {                                                                                                                                                     
virtual bool handleOccurrence() {                                                                                                                                                                         
}                                                                                                                                                                                                         
};                                                                                                                                                                                                        
__extension__ extern template class opt<unsigned>;                                                                                                                                                        
template class opt<unsigned>;
Comment 25 Douglas Gregor 2010-03-02 13:27:17 PST
Super-important for 2.7
Comment 26 Rafael Ávila de Espíndola 2010-03-02 13:31:40 PST
Created attachment 4363 [details]
patch

This is probably a hack, but it fixes the previous failure. Looks like needsVtable is being called only once for

__extension__ extern template class opt<unsigned>;                                                                                                                                                        
template class opt<unsigned>;
Comment 27 Rafael Ávila de Espíndola 2010-03-02 15:41:30 PST
This testcase was fixed by revision 97589:

$ clang++ -c test.cpp
$ nm test.o | grep _ZN8_Rb_treeIPK4Decl4pairIKS2_9CharUnitsE10_Select1stIS6_E4lessIS2_E9allocatorIS6_EED1Ev
0000000000000000 W _ZN8_Rb_treeIPK4Decl4pairIKS2_9CharUnitsE10_Select1stIS6_E4lessIS2_E9allocatorIS6_EED1Ev
Comment 28 Rafael Ávila de Espíndola 2010-03-02 15:42:06 PST
Trying to mark as fixed ....
Comment 29 Rafael Ávila de Espíndola 2010-03-02 15:42:32 PST
Trying again...