This is the mail archive of the gcc@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]
Other format: [Raw text]

speculative instantiation and diagnostics


Hi,
Jason recently forwarded me some mail concerning a boost diagnostic. I attach
that here, along with my reply. Upon further investigation, there are two
separate problems here, and in trying to fix them, I ran into an existing
gcc problem.

Problem 1. (The language lawyer bit). As I said in my followup, I believe that
the resolution of DR 295 breaks existing code. Upon further reflection, I see
why ignoring the cv quals on a reference type don't give the same problem. For
a template call such as 'foo (someobject)', We'd deduce a T as the type of the
object, rather than a reference to type -- you have to explicitly specify the
template as 'foo (T &)' to get a reference parameter. The same doesn't happen
for a call 'foo (somefunction)'. We'd deduce a T as the type of the function
(rather than decay to pointer to function. Thus you have to try quite hard to
get template parameters of reference type ('foo<typeofobject &> (someobject)'
would do it), but it is easy to get template parameters of function type.
When you do get a reference type parameter, it is easy to give a specialization
which applies only to such parameters - which is what boost does,
	template <typename T> struct<T &> { ...}
yoy can't do that for plain function types. There's no way to specify that
a template type parameter must be a function type - you can eliminate
all scalar, reference, pointer and array types. You get left with aggregate
and function types. To distinguish those you need to extend C++, and one
way to do that might be allow a template parameter to match a function arg
list, and a suitable syntax might be to use ...
	template <typename R, ... ARGS> void Func (R (*parm) (ARGS))
this would deduce 'parm' to be a pointer to function taking ARGS and
returning R. You'd also permit things like
	template <typename T, typename F, ... ARGS> void Func (R (*parm) (F, ARGS))
etc. Of course I'm not suggesting we do this now, but it seem to be the
way to patch a hole in the type deduction machinery.

The fix for the moment should be to not implement DR 295

Problem2. (new problem) Ignoring the cv qualifiers on a reference type still
can give a warning during type deduction in the following code
	
	template <typename T> struct Canonicalize {//#a
	  typedef T Canonical;
	};
	template <typename T> struct Canonicalize<T const volatile> { //#b
	  typedef T Canonical;
	};

	template <typename C, typename T>
	typename Canonicalize<C &>::Canonical foo (C *p, T *) { // #1
	  return *p;
	}
	typedef void (*Ptr) ();

	template <typename T> int foo (Ptr, T *) { // #2
	  return 0;
	}

	void Foo () {};

	int main () {
	  return foo (Foo, (void *)0); //#3
	}

at the call #3, both templates #1 and #2 can be deduced, but #2 wins
because it is more specialized. However, in deducing #1 we instantiate
'Canonicalize<someT &>', that pick #b because it is more specialized
than #a, and of course tries to make a volatile reference. We issue
a warning for that -- which is completely confusing, repeated and
inappropriate. The problem is that we instantiate the return type of
#1 with an effective COMPLAIN parm of errors & warnings.

Problem3. In fixing that I came across a long standing problem. In
instantiaing non-deduced function parms & return type, we generate errors
for uninstantiable template types -- we should not do that, but simply
reject that deduction.

In trying to fix 2 & 3, I ended up passing the template routine's COMPLAIN
parameter to a frighteninly large number of routines -- and I'm not
convinced I've covered every place that a diagnostic could pop out. This
became unsatisfactory.

Thinking how to solve this properly you'd naturally throw an exception
at an error site, and allow the catcher to process the diagnostic -- iff
C supported exceptions! That wouldn't deal with the warnings problem. For
that you'd want to have some global variable to inhibit them. Then we have
to know whether to emit them once we select the correct instantiation.

Mark, it occurs to me that this is a similar requirement to the speculative
parsing that the new parser must do, so a similar machinery should be
used for both. Also the compiler's full of annoying checks for
error_mark_node.  Starting from a clean slate, something like
	if (push_error_handler (&errorcatcher)) {
		handle error from do stuff
		either rethrow (errorcatcher)
		or return
	}
	... do stuff
	pop_error_handler ()
This is something that could be implemented with setjmp/longjmp. And we'd
need some way of squirrelling away a diagnostic, without the overhead of
actually formatting it (until it really is emitted)

Anyway, that too is far too much to do right now.

So I propose to fix problem2, by not warning for volatile references.

thoughts? sorry, this got a bit rambly

nathan

--
Dr Nathan Sidwell   ::   http://www.codesourcery.com   ::   CodeSourcery LLC
         'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org

--- Begin Message ---
Nathan, I think this was broken by your 2002-02-22 unification changes.

--- Begin Message ---
[boost] Type traits and a bug in gcc3.2Hi,

Partial specialization in the new GCC seems to be broken for
function types. This came up with LL, which triggered some warnings
in type_traits.



Here's a small test prgoram:

// -----------------------------------------

#include "boost/type_traits.hpp"
#include <iostream>



typedef void(T)(int); // T here is plain function type void()(),
boost::remove_cv<T>::type& f = foo;



int main() {};

// -----------------------------------------

And these are the error messages

/u/jajarvi/boost/boost/type_traits/cv_traits.hpp: In instantiation of
`boost::detail::cv_traits_imp<void (*)(int)>':
/u/jajarvi/boost/boost/type_traits/cv_traits.hpp:128:   instantiated from
`boost::remove_cv<void ()(int)>'
type_traits.cpp:6:   instantiated from here
/u/jajarvi/boost/boost/type_traits/cv_traits.hpp:73: warning: ignoring `
   volatile' qualifiers on `void ()(int)'
/u/jajarvi/boost/boost/type_traits/cv_traits.hpp:74: warning: ignoring `
   volatile' qualifiers on `void ()(int)'
/u/jajarvi/boost/boost/type_traits/cv_traits.hpp:72: warning: ignoring `
   volatile' qualifiers on `void ()(int)'
/u/jajarvi/boost/boost/type_traits/cv_traits.hpp:72: warning: ignoring `
   volatile' qualifiers on `void ()(int)'
/u/jajarvi/boost/boost/type_traits/cv_traits.hpp:75: warning: ignoring `
   volatile' qualifiers on `void ()(int)'
type_traits.cpp:6: `foo' was not declared in this scope

It seems that the compiler picks the wrong specialization for
cv_traits_imp (one with volatile qualifier).

Here is another test program to confirm this:

// --------------------------------------------------------
#include <iostream>

template <class T> class A {
public:
  static void foo () { std::cout << "Not volatile"; }
};

template <class T> class A<volatile T> {
public:
  static void foo () { std::cout << "Volatile"; }
};

template <class T>
void x(T& t) {
  A<T>::foo();
  // T here should be a plain function type int()()
  // The compiler (3.20) picks the specializatoin for A<volatile T> ???
}

int bar() {};

int main() {
  x(bar);
};

// ---------------------------------------------------------

Compiling the program with gcc3.2 gives similar warnings and when
executed, prints out "Volatile".

I've reported the bug to GNATS.

Any suggestions for a fix for type traits until it gets fixed?

Cheers, Jaakko



_______________________________________________
Unsubscribe & other changes:
http://lists.boost.org/mailman/listinfo.cgi/boost
--- End Message ---
--- End Message ---
--- Begin Message ---
Jason Merrill wrote:
> 
> Nathan, I think this was broken by your 2002-02-22 unification changes.
oh crap. This means the resolution of DR 295 is broken.
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#295

> template <class T> class A {
> public:
>   static void foo () { std::cout << "Not volatile"; }
> };
> 
> template <class T> class A<volatile T> {
> public:
>   static void foo () { std::cout << "Volatile"; }
> };
> 
> template <class T>
> void x(T& t) {
>   A<T>::foo();
>   // T here should be a plain function type int()()
>   // The compiler (3.20) picks the specializatoin for A<volatile T> ???
> }
> 
> int bar() {};
> 
> int main() {
>   x(bar);
> };
The two candidates are 
T = int () ();
T = int (volatile) () -> volatile ignored via 295 so T = int () ()

So both are candidate template specializations. We pick the most specialized
one. IMO, that is undesirable for volatile (but not necessarily for const).

The rationale behind 295 was so that given
tpl (T *) and tpl (T const *)
a function pointer would pick the latter.

<ramble>
It's kind of wierd that const and volatile are both treated in the same way
by the cv-qual machinery, whereas to a programmer const is *adding* 'stability'
whereas 'volatile' is *subtracting* 'stability.

Why do we get such difficulty when ignoring cv-quals on function types, but
not for reference types? I think it's because the deduction algorithm treats
deductions references differently - the reference must be specified explicitly
in the template, but that's not true for deducing a function type. You
can always write a template that will 'select out' all the reference cases,
but you can't write one which will do the same for function types.
</ramble>

It appears that 295 has to become NAD - the solution does more harm than good.

thoughts?

nathan

-- 
Dr Nathan Sidwell :: Computer Science Department :: Bristol University
           The voices in my head told me to say this
nathan@acm.org  http://www.cs.bris.ac.uk/~nathan/  nathan@cs.bris.ac.uk
--- End Message ---

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