Topic: Template Recursion "failure


Author: stephen.clamage@sun.com (Steve Clamage)
Date: Wed, 12 Nov 2003 03:02:08 +0000 (UTC)
Raw View
The standard does not dictate the behavior of compilers when error
conditions or system limits are reached. The part of the standard you
quote puts programmers on notice that compilers are not expected to have
unlimited resources.  (Refer also to Appendix B.)

A program that exceeds a compiler limit can be expected to fail somehow.
The "how" is considered a "quality of implementation" issue.

The case of deep template recursion is a bit of a problem in itself.

If the compiler doesn't allow deep recursion, some useful programs don't
compile. (Recent programming techniques like meta-programming require
deeper template recursion than was contemplated when the standard was in
development.)

But if the compiler places no limit on template recursion, it doesn't
catch the case of non-terminating recursion. The compiler would keep on
generating template instances until it ran out of memory or disk space.
The "error report" would consist of the compiler taking a very long time
apparently doing nothing, and finally aborting. To provide better
diagnostics, compilers impose what the designers hope is a reasonable
limit on recursion.

Detecting non-terminating template recursion is in fact the halting
problem, so I wouldn't expect a better solution any time soon. :-)

--
Steve Clamage, stephen.clamage@sun.com

Jim Apple wrote:
> 14.7.1/14 says "There is an implementation-defined quantity that
> specifies the limit on the total depth of recursive instantiations,"
>
> What happens when this limit is reached?  Must the following fail at
> compile time:
>
> struct any {
>   template <typename T>
>   operator T();
> } ANY;
>
>
> template<typename T, unsigned N>
> char (*func(typename T::template desc<N>, T(*)[N]))[2*N];
>
>
> struct emp { template<unsigned N> struct desc;};
>
>
> char (*func(float,...))[1];
>
>
> template<unsigned N>
> struct emp::desc {
>   static emp (*data)[N-1];
>   static const unsigned val = sizeof(*func(ANY, data));
> };
>
>
> template<>
> struct emp::desc<1> {};
>
>
> template<unsigned N>
> emp (*emp::desc<N>::data)[N-1] = 0;
>
> emp (*A)[/*Some Very Large Number*/];
> char (*B)[sizeof(*func(ANY,A))];
>
> At some point, a recursion limit is reached, and emp::desc<N-1> can't be
> instantiated.  Then, it is not a type in emp, so SFINAE kicks in, the
> templated func is not added to the overload set, and and sizeof(*B) is 1.
>
> ICC 7.1, g++ 3.3.1, and Comeau online all bork.  What part of the
> standard requitres them to do so?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kuyper@wizard.net (James Kuyper)
Date: Wed, 12 Nov 2003 03:02:14 +0000 (UTC)
Raw View
jpearapplebanana@freeshell.org (Jim Apple) wrote in message news:<bop2db$9fl$1@news.wplus.net>...
> 14.7.1/14 says "There is an implementation-defined quantity that
> specifies the limit on the total depth of recursive instantiations,"
>
> What happens when this limit is reached?

That's not defined by the standard, which means that just about
anything could happen. As a practical matter, a poorly written
compiler might either get stuck in an infinite loop or fail
catastrophically; a well-written one will give you an error message of
some kind.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Wed, 12 Nov 2003 19:54:53 +0000 (UTC)
Raw View
In article <bor46q$qfe$1@news1nwk.SFbay.Sun.COM>, Steve Clamage
<stephen.clamage@sun.com> writes
>But if the compiler places no limit on template recursion, it doesn't
>catch the case of non-terminating recursion. The compiler would keep on
>generating template instances until it ran out of memory or disk space.
>The "error report" would consist of the compiler taking a very long
>time apparently doing nothing, and finally aborting. To provide better
>diagnostics, compilers impose what the designers hope is a reasonable
>limit on recursion.
>
>Detecting non-terminating template recursion is in fact the halting
>problem, so I wouldn't expect a better solution any time soon. :-)

A couple of options spring to mind:

Have the compiler check periodically whether the programmer wants to
continue deeper into a recursion.

Have a facility by which the user can specify a recursion depth either
on a per compile or a per instance basis.

Both are QoI issues so it is up to compiler implementors to decide if
they wish to provide such facilities and up to users to make clear
whether such facilities would be desirable.


--
Francis Glassborow      ACCU
If you are not using up-to-date virus protection you should not be reading
this. Viruses do not just hurt the infected but the whole community.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpearapplebanana@freeshell.org (Jim Apple)
Date: Fri, 14 Nov 2003 02:36:50 +0000 (UTC)
Raw View
Steve Clamage wrote:
> The standard does not dictate the behavior of compilers when error
> conditions or system limits are reached.

That is very helpful information, indeed.  Wehre can I find that in the
standard?

> A program that exceeds a compiler limit can be expected to fail somehow.

What I'm asking is, "why can't the compiler prevent the exceeding (not a
word!) of the limit when possible?"  The example in the original
question is almost the only example I can think of that would allow
failure.  I would expect that this would have to fail:

template<unsigned N>
struct joy : public joy<N-1> {};

template<>
struct joy<1> {};

joy</*bignum*/> x;

This fails because I can't declare x without a type.  In the example I
gave, it is legal for that function prototype to fail at substitution
time under the limited provisions of SFINAE in 14.7.2.

> The "how" is considered a "quality of implementation" issue.

Are you saying that the parent code's failure is not mandated by the
standard either way?

 > [halting problem stuff snipped]

Of course it must terminate at some point.  I'm just wondering if
implementers have the liberty of doing so gracefully.

Jim
--
to email me, use only the 'j' followed by the red fruit

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kuyper@wizard.net (James Kuyper)
Date: Fri, 14 Nov 2003 17:48:49 +0000 (UTC)
Raw View
jpearapplebanana@freeshell.org (Jim Apple) wrote in message news:<bosc42$p2j$1@news.wplus.net>...
> Steve Clamage wrote:
> > The standard does not dictate the behavior of compilers when error
> > conditions or system limits are reached.
>
> That is very helpful information, indeed.  Wehre can I find that in the
> standard?

14.7.1p14: "There is an implementation-defined quantity that specifies
the limit on the total depth of recursive instantiation, which could
involve more than one template. The result of infinite recursion in
instantiation is undefined."

It also fails to define the behavior of exceeding that limit, with a
finite recursion.
You might also want to look at Annex B, which lists "implementation
properties", and also fails to describe the behavior that occurs when
such limits are exceeded.

I can't point you at the location where it fails to define the
behavior, for the same reason I can't point you at the place where the
US Constitution fails to describe TV sets. However, the standard does
say in section 1.3.12: "... Undefined behavior may also be expected
when this International Standard omits the description of any explicit
definition of behavior. ..."

> > A program that exceeds a compiler limit can be expected to fail somehow.
>
> What I'm asking is, "why can't the compiler prevent the exceeding (not a
> word!) of the limit when possible?"  The example in the original

It can. It's just not required to do so. The standard is deliberately
designed to allow both high-powered implementations which read your
mind in order to detect and warn you about any failure of your code to
say exactly what you intended it to do, and quick and dirty
implementations which fail catastrophically without warning whenever
you use them in a way that has undefined behavior. Of course, the
quick and dirty implementations are also likely to fail occasionally
even when used correctly - but the standard does not condone that. The
difference between the mind-reading and quick-and-dirty
implementations is called "Quality of Implementation", and the
standard deliberately does not specify the level of quality required.

...
> > The "how" is considered a "quality of implementation" issue.
>
> Are you saying that the parent code's failure is not mandated by the
> standard either way?

Correct. The standard never mandates failure.

> Of course it must terminate at some point.  I'm just wondering if
> implementers have the liberty of doing so gracefully.

Of course. Undefined behavior includes that as one of the infinite
range of possibilities.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Fri, 14 Nov 2003 21:25:14 +0000 (UTC)
Raw View
In article <bosc42$p2j$1@news.wplus.net>, Jim Apple
<jpearapplebanana@freeshell.org> writes
>> A program that exceeds a compiler limit can be expected to fail somehow.
>
>What I'm asking is, "why can't the compiler prevent the exceeding (not
>a word!) of the limit when possible?"  The example in the original
>question is almost the only example I can think of that would allow
>failure.  I would expect that this would have to fail:

We simply cannot mandate that the compiler detect in advance that it
will exceed some resource limit because that would need a degree of code
analysis that itself would have potential for exceeding resource limits.

Once resource limits have been exceeded all bets are off because we
cannot predict what damage may be done in some environments by a process
that runs off the end of its allocated resources. Some systems will
simply close down the process but on some more primitive systems
irreparable damage may have been done with complete close down of the
hardware being the only viable recovery mechanism.


--
Francis Glassborow      ACCU
If you are not using up-to-date virus protection you should not be reading
this. Viruses do not just hurt the infected but the whole community.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpearapplebanana@freeshell.org (Jim Apple)
Date: Mon, 10 Nov 2003 23:14:14 +0000 (UTC)
Raw View
14.7.1/14 says "There is an implementation-defined quantity that
specifies the limit on the total depth of recursive instantiations,"

What happens when this limit is reached?  Must the following fail at
compile time:

struct any {
   template <typename T>
   operator T();
} ANY;


template<typename T, unsigned N>
char (*func(typename T::template desc<N>, T(*)[N]))[2*N];


struct emp { template<unsigned N> struct desc;};


char (*func(float,...))[1];


template<unsigned N>
struct emp::desc {
   static emp (*data)[N-1];
   static const unsigned val = sizeof(*func(ANY, data));
};


template<>
struct emp::desc<1> {};


template<unsigned N>
emp (*emp::desc<N>::data)[N-1] = 0;

emp (*A)[/*Some Very Large Number*/];
char (*B)[sizeof(*func(ANY,A))];

At some point, a recursion limit is reached, and emp::desc<N-1> can't be
instantiated.  Then, it is not a type in emp, so SFINAE kicks in, the
templated func is not added to the overload set, and and sizeof(*B) is 1.

ICC 7.1, g++ 3.3.1, and Comeau online all bork.  What part of the
standard requitres them to do so?
--
to email me, use only the 'j' followed by the red fruit

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]