[Types-sig] type-assert operator optimizations

Greg Stein gstein@lyra.org
Wed, 22 Dec 1999 11:57:20 -0800 (PST)


On Thu, 23 Dec 1999, skaller wrote:
> Greg Stein wrote:
> > > This means that the x!t can be optimised to x,
> > > without affecting strictly conforming program
> > > semantics.
> > 
> > If the compiler can definitively state that the test will never fail, then
> > it doesn't have to include a runtime check.
> > 
> > If the compiler can definitively state that the test will always fail,
> > then it can issue an error and refuse to compile.
> > [ with the caveat of catching exceptions ]
> > 
> > If the compiler believes that it might fail in some cases, then it could
> > issue a warning (and go ahead and insert a runtime check).
> > [ and yes, there can be switches to avoid issuing warnings ]
> 
> These semantics are 
> 
> 	(a) incoherent/inconsistent

They're fine to me :-)

> 	(b) not the same as what I propose

Tough. I proposed it first. :-)

> I want to explain both points. (b) first:
> I propose that if the test were to fail at any point
> in the execution of the program, the program is invalid,

Sorry, but that is with your "exceptions-are-evil" model, and we've
covered that before. Exceptions are part of Python, and their rigorous
specification is also part of Python.

The type-assert operator is *defined* to raise an exception if the type is
wrong. Period.

There are no statements about the validity of the program.

>... point moot; I disagree with the premise ...
> 
> Now for (a). There is an assumption: that there is no definite
> algorithm given for deducing if the test will fail.

If the compiler sees:

  x = 1 ! type(1)

Assuming that "type" cannot be altered, then the compiler can most
assuredly elide the check because it knows it will always succeed.

Given:

  x = 1 ! type("")

It will always fail, and the compiler may as well stop right there.
[ with the caveat of possible switches that turn this in a warning or
  ignore it or whatever... ]

I do grant you, however, that in most cases the compiler *cannot* make a
definitive statement about what will happen at runtime with that operator.
So it just puts the assertion code in, and continues on. No biggy.
[ personally, I probably wouldn't even bother with trying to elide the
  code; the type checker could easily comment on it, though ]

> In this case, it is possible that compiler (A) deduces
> the test will always fail, and rejects the program,
> while compiler (B) isn't smart enough, and compiles code
> to raise an exception. In this case, a programmer
> may catch the exception and handle it, and this
> behaviour would be required of the language.
> But that is NOT the behaviour (A) produced.

Okay. Let's work on the assumption that we have different behaviors
from the compilers.

> If one compiler can reject the program, it CANNOT be
> a valid program, and in that case, a requirement
> on a compiler (that it throw an exception if it is dumb)
> cannot be made to stick, since the program is in error.

If one compile rejects it, then it is just smarter. It doesn't say
anything about the validity of the program.

---- foo.py

raise "an exception"

----

I maintain the above is an entirely valid program. Your smart compiler
might say "sorry, that always raises an exception, so I'm not going to
bother compiling it." Fine. But that doesn't alter the fact the program is
valid.

> I think you must decide that the semantics require
> a run time error ALWAYS, or, that the test can
> be elided ALWAYS. There is no half way ground.

Yes, there is a half-way ground.

In the "exceptions-are-evil" model, maybe not. But in the standard Python
model, I can certainly elide tests that I know will always succeed.
Granted: it is hard to know that, but when I *can*, then I'm free to
remove the tests. Runtime errors are fine, and they do not imply that the
program is in error and the compiler should have rejected it. It would
*nice* if the compiler did, but life is a bitch.

Very few C compilers will complain about:

void main(void) {
  *(int *)0 = 5;
}

But it certainly bombs quite quickly.

> The current requirements for assertion statements
> are, effectively, that the test can be elided,
> and therefore, invalid programs exist. The fact
> that the current CPython interpreter in non-optimising
> mode raises an exception is nicety of that particular
> implementation, not a requirement of the language.

IMO, you have this entirely wrong. CPython defines the language (where it
isn't explicit in the language and library reference manuals). The fact
that exceptions are raised means they are part of the language, and
therefore a requirement.

Guido has been using his JPython chips every now and then to define the
language, but generally speaking: CPython is the definition and states the
requirements for all Python implementations.

> I'm assuming 
> 
> 	1) there is ONE python language
> 	2) both the optimising and non-optimising
> 		byteocode compiler conform to the semantics
> 
> and from this I deduce the above language semantics.
> Remember language semantics are constraints on translators,
> they're not specifications of what a particular tool does
> in cases that no particular behaviour is required.

Your two assumptions are correct. But I think you're assuming the wrong
semantics.

Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/