This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

re: c++/3708: Zealous template expansion


For reference
==============The offending code=================
===============WRONG2.c++========================
class Foo
{
public:
   Foo() {};

   Foo(int) {};
};

class Bar
{
public:
   Bar() {};

   template <class T>
   operator T ()
   {
   };
};


class BarBar : public Bar
{
public:
   BarBar () {};

   operator Foo ()
   {
   };
};


int main()
{
   BarBar black_sheep;

   Foo soo;
   soo = (Foo) black_sheep;

   return 0;
}
===========end of offensive stuff================

OK, I've tracked this to the following;

sprinkle some printouts around.....(who need a debugger for a simple
compiler ;)

=====gcc/cp/call.c CVS version 1.247 line 2497=========
============in build_user_type_conversion_1=============
  fprintf(stderr,"BUT1PREsplice\n");
      print_z_candidates (candidates);
  candidates = splice_viable (candidates);
  fprintf(stderr,"BUT1POSTsplice\n");
      print_z_candidates (candidates);
  cand = tourney (candidates);
  fprintf(stderr,"BUT1tour\n");
      print_z_candidates (cand);
  fprintf(stderr,"\n\n");
========================================================

=====gcc/cp/call.c CVS version 1.247 line 3400=========
===========in build_new_op=============================
  fprintf(stderr,"BNO1PREsplice\n");
      print_z_candidates (candidates);
  candidates = splice_viable (candidates);
  fprintf(stderr,"BNO1POSTsplice\n");
      print_z_candidates (candidates);
  cand = tourney (candidates);
  fprintf(stderr,"BNO1tour\n");
      print_z_candidates (cand);
  fprintf(stderr,"\n\n");
========================================================

Compile the offensive code with g++ which will call cc1plus to produce
the output

=============================================
BUT1PREsplice
WRONG2.c++: In function `int main()':
WRONG2.c++:18: candidates are: Bar::operator T() [with T = int]
BUT1POSTsplice
WRONG2.c++:18: candidates are: Bar::operator T() [with T = int]
BUT1tour
WRONG2.c++:18: candidates are: Bar::operator T() [with T = int]


BUT1PREsplice
WRONG2.c++:29: candidates are: BarBar::operator Foo()
WRONG2.c++:18:                 Bar::operator T() [with T = Foo]
WRONG2.c++:4:                 Foo::Foo(const Foo&)
WRONG2.c++:8:                 Foo::Foo(int)
BUT1POSTsplice
WRONG2.c++:29: candidates are: BarBar::operator Foo()
WRONG2.c++:18:                 Bar::operator T() [with T = Foo]
BUT1tour
WRONG2.c++:29: candidates are: BarBar::operator Foo()
WRONG2.c++:18:                 Bar::operator T() [with T = Foo]


WRONG2.c++:39: call of overloaded `Foo(BarBar&)' is  ambiguous
WRONG2.c++:4: candidates are: Foo::Foo(const Foo&)
WRONG2.c++:8:                 Foo::Foo(int)
=================================

We see that BarBar::operator Foo() is indeed seen, and that all other
candidates eventually boil down to
Bar::operator T(), which is overridden for T=Foo in BarBar.

The compiler first attempts a copy constructor of Foo. This is the
beginning of the end.
It then finds Foo::Foo(const &Foo) and Foo::Foo(int).
Next, it seeks a conversion from BarBar to Foo or from BarBar to int. It
finds both in the form
of Bar::operator T () with T=[Foo and int], which explains the
Bar::operator T() attempts.
In the interim, it also finds BarBar::operator Foo(), and promptly
ignores its significance. 
The decision to do this seems to be after the exit from
build_user_type_conversion_1 (after
the printouts and tournament), in implicit_conversion. I'm not sure, but
it seems to extract
the second candidate Bar::operator T() [with T = Foo], discarding
BarBar::operator Foo().
The return then goes to reference_binding, where the candidate is linked
into a Foo temporary node,
and passed up to implicit_conversion. build_new_method_call then takes
this and jousts it with
Foo::Foo(Bar::operator T() [with T = int]) and decides this is
ambiguous, which is true if
you ignore BarBar::operator Foo().

The problem of resolving such difficulties is quite general. It is a
unification of
a graph with a another graph, with occurs check. The algorithm for doing
this in linear time
without resorting to local search cuts like the above have been known
for some time now.
The origin of the problem above is the pruning of the unification, based
upon the first
choice of strategy (go for a constructor first), and excluding other
possibilities, even those
actually encountered in the course of the search. Much more efficient, 
robust and general solutions already exist, and often require simpler
code.

In the short term, it is difficult to see how to improve the situation
(BTW, please, no RFC quoting,
that's for committees). Perhaps a little re-ordering of the priorities
of the strategies
might be possible, say look for an explicit conversion op first, before
the constructor of the
target is tried. More generally, it might be better to find a way of
gathering more candidates
for the joust, they have been found anyway.....


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]