Erlang Mailing Lists

Author Message

<  Erlang questions mailing list  ~  Small poll

D.WILLIAMS at csee-transp
Posted: Thu Dec 11, 2003 9:21 am Reply with quote
Guest
> In a function like:
>
> test(A) ->
> a + 42.
>
> which is either crap (arguably) or a typo (A vs a), how many
> Erlang users:
>
> 1. Are content with the current situation where the compiler
> happily compiles this program
> 2. Would like to see a warning, but a .beam file generated
> nevetheless
> 3. Would prefer if the compiler in R10 refused to compile it

Hello,

I am quite content with the current situation. I always compile
with [warn_unused_vars], and even if A were used elsewhere I would
detect the runtime error in my unit tests.

Please don't make compile times any longer!

Regards,

Dominic Williams.


Post generated using Mail2Forum (http://m2f.sourceforge.net)
bjorn at erix.ericsson.se
Posted: Thu Dec 11, 2003 9:25 am Reply with quote
Guest
We in the Erlang/OTP group at Ericsson have also discussed
this question recently.

For the R10B release, we will probably change the compiler
according to #2 or #3. It is not hard that hard to do. What
we will do is to keep line number information a bit longer
so that the optimization passes that can easily detect this
kind of type error also has access to the original line number
when writing the message.

In the Erlang/OTP group, there are different opinions whether
#2 or #3 is the best way to go.

Personally, I think that #2 is the way to go, but most others
in the Erlang/OTP group seem to favor #3.

/Bj
pascal.brisset at cellici
Posted: Thu Dec 11, 2003 9:30 am Reply with quote
Guest
> 1. Are content with the current situation where the compiler
> happily compiles this program
> 2. Would like to see a warning, but a .beam file generated
> nevetheless
> 3. Would prefer if the compiler in R10 refused to compile it

I wanted to answer "3, of course" until I realized the
follow-up poll questions might be embarrassing.


- Suppose we reject a+42. Then what about:
test(A) -> a<42.
This is exactly the same typo. Can we also detect it and still
allow < between arbitrary terms (e.g. keys in data structures) ?

The ML way would be to declare < with the type:
'<'(T,T) -> bool().
and allow the programmer to explicitly widen the type of
operands to term() when needed. But this does not make sense
without a clear notion of type inference (i.e. what is the type
of 'a' ? atom() ? The atom 'a' ? a|42 ?).


- More generally, the verification will not be complete.
How do we explain the rationale when a programmer complains
"Hey, the compiler caught this mistake, but not that one" ?


- Should the compiler also reject:
test(A) ->
if a+42 == 0 -> true;
a+42 =/= 0 -> false;
true -> fish
end.
In other words, do people occasionally rely on the fact that
exceptions are silently ignored in guards, possibly in more
elaborate ways than above ?


All things considered, obviously even a local, partial
typechecker would be welcome.

And since Christmas is approaching, #1 on my wishlist would be
a tool which unifies in a consistent way:
- a syntax for function and data type declarations
- erldoc
- typechecking
- uniqatom
- xref
- user-definable, typed behaviours, as in:
%% File: gen_server.bhv
+defbehaviour gen_server(InitArg, State, Request, Result).
+type handle_call(Request, {pid(),term()}, State) ->
{reply, Result, State}
| {reply, Result, State, timeout_val()}
| {noreply, State}
| {noreply, State, timeout_val()}
| {stop, Reason, Result, State}
| {stop, Reason, State}.
- warnings related to the emulation of destructive updates with
single assignment, as in:
handle_call({set,NewValue}, From, State) ->
State1 = State#state{val=NewValue},
State2 = State1#state{timestamp=now()},
{reply, ok, State1}. % Catch this.

-- Pascal



Post generated using Mail2Forum (http://m2f.sourceforge.net)
Erik.Stenman at epfl.ch
Posted: Thu Dec 11, 2003 9:41 am Reply with quote
Guest
You must be kidding, right?

As it is now the compiler always does constant propagation and folding.
If you write:
A = 1 + 2,
you get
A = 3,
in the compiled beam code.
(These types of expressions do come up, especially if you are using
macros...)

The problem is that if you write
a + 1,
then the compiler has to spend extra time compiling the expression, handling
the
special case that constant folding does not work, and produce larger
code than necessary.

> I would not like having anyone wasting their valuable time
> improving the compiler to catch this

This already has to be cached and handled by the compiler.

> and also wasting more
> CPU power on everyones computer when compiling.

More CPU power is probably used today in order to 'fix' this
special case, at least for native code.

Erik

> -----Original Message-----
> From: owner-erlang-questions_at_erlang.org
> [mailto:owner-erlang-questions_at_erlang.org] On Behalf Of Peter Lund
> Sent: Thursday,11 December, 2003 09:11
> To: kostis_at_user.it.uu.se
> Cc: erlang-questions_at_erlang.org
> Subject: Re: Small poll
>
> I am certainly putting my money on no 1!
>
> The case when "a + 32" craches my code will be easily
> detected the first time I run that piece of code, and since I
> *do* test my code before delivery, this is a non-problem. I
> would not like having anyone wasting their valuable time
> improving the compiler to catch this and also wasting more
> CPU power on everyones computer when compiling.
>
> /Peter
>
> > which is either crap (arguably) or a typo (A vs a), how many Erlang
> > users:
> >
> > 1. Are content with the current situation where the compiler
> > happily compiles this program
> > 2. Would like to see a warning, but a .beam file generated
> > nevetheless
> > 3. Would prefer if the compiler in R10 refused to compile it
> >
> > Notice I am not talking about any serious attempt to static type
> > checking, but for really "basic" checks.
> >
> > Kostis.
>
>
>
>



Post generated using Mail2Forum (http://m2f.sourceforge.net)
peter at lundata.se
Posted: Thu Dec 11, 2003 10:13 am Reply with quote
Guest
If the effort (in sence of valuable time) already has been made,
then it is obviously a good thing to have it doing 2 by default
and 3 by adding some compiler directive.

Erik Stenman wrote:
> You must be kidding, right?
>
> As it is now the compiler always does constant propagation and folding.
> If you write:
> A = 1 + 2,
> you get
> A = 3,
> in the compiled beam code.
> (These types of expressions do come up, especially if you are using
> macros...)
>
> The problem is that if you write
> a + 1,
> then the compiler has to spend extra time compiling the expression,
> handling the
> special case that constant folding does not work, and produce larger
> code than necessary.
>
>> I would not like having anyone wasting their valuable time
>> improving the compiler to catch this
>
> This already has to be cached and handled by the compiler.
>
>> and also wasting more
>> CPU power on everyones computer when compiling.
>
> More CPU power is probably used today in order to 'fix' this
> special case, at least for native code.
>
> Erik





Post generated using Mail2Forum (http://m2f.sourceforge.net)
vlad_dumitrescu at hotmai
Posted: Thu Dec 11, 2003 10:18 am Reply with quote
Guest
Erm, a side question: since the question comes from the Hipe group, I feel
compelled to check if it refers to the compiler in general or just the Hipe
variant?

regards,
Vlad


Post generated using Mail2Forum (http://m2f.sourceforge.net)
kostis at user.it.uu.se
Posted: Thu Dec 11, 2003 10:42 pm Reply with quote
Guest
Glad to receive so many responses. Let me reply on a few of them.


> On Wed, 10 Dec 2003, Vlad Dumitrescu wrote:
>
> > > test(A) ->
> > > a + 42.
> >
> > For the "A vs a" problem, there is already a warning being issued. Possibly
> > the full warnings options should be enabled by default?

First of all, let's disconnect this thread from "unused_variables"
(although the warning **SHOULD** be turned on by default). Let's
say that the program was:

test(A) ->
bar(A),
a + 42.

> > Since both a and 42 are constants, I feel it would be easy (besides useful)
> > to be able to detect it at compile time. The operation should even be
> > evaluated at compile time, IMHO.
>
> Exactly. And if compile time evaluation fails, give compile time error
> {badarith,....}.

Regardless of whether this is evaluated at compile time or not, there
are still three options as I wrote in my previous mail:

1. the compiler happily compiles this program, but simply replaces
a + 42 with a call to spit {badarith,....}
2. generate a warning, but also byte code.
3. the compiler refuses to compile such erroneous code.

I will try to make a case for 3 below.

Option 1, seems strange to me: The compiler has found out that a particular
evaluation will result in an error, but does not inform its user about it
and moreover generates more code (in terms of space) than that needed for
the expression "a + 42" (if the expression is statically evaluated to
badarith) or refuses to do work at compile time, leaving this work for
run-time (which is what the BEAM compiler currently does).

Option 2 on the surface seems a reasonable choice, but is it really ?
I claim that at least 90% of all such cases are programming errors.
As Taavi mentioned, if the programmer _really_ wants to generate an
error, there is throw() and the a + 42 can be its argument (either
as a string or as a tuple). Seems much, much cleaner to me.

Now let me tell you why I am interested in 3:

In the context of generating native code, it is really a nuisance
to have to handle all sorts of illegal combinations of arguments
to "basic" primitives like "+". Moreover, in the case of HiPE,
because the compiler is (much) more sophisticated than the BEAM
one, it actually discovers quite a lot more cases of such errors.
Currently, we try our best to "immitate" the behaviour of the BEAM
compiler, but in sleepless nights like this one, I keep wondering
whether there should really be a limit to the level of naivety
one pretends to show, or shouldn't there be?

(Just in case it is not obvious, this is a more general issue,
not just related to the simple "a+42" case I chose to illustrate
my point.)

Cheers,
Kostis.

PS. I am curious whether Eric Newhuis could elaborate on:

> But we presently rely on this behavior and do not
> treat it as a warning.

Exactly how do you "rely" on this?



Post generated using Mail2Forum (http://m2f.sourceforge.net)
mbj at nortelnetworks.com
Posted: Thu Dec 11, 2003 10:43 pm Reply with quote
Guest
Kostis Sagonas <kostis_at_user.it.uu.se> wrote:
> I was wondering whether the Erlang user community would care
> to comment on the following:
>
> In a function like:
>
> test(A) ->
> a + 42.
>
> which is either crap (arguably) or a typo (A vs a), how many
> Erlang users:
>
> 1. Are content with the current situation where the compiler
> happily compiles this program
> 2. Would like to see a warning, but a .beam file generated
> nevetheless
> 3. Would prefer if the compiler in R10 refused to compile it


I vote for #2 (followed by #3 + flag to force compilation, followed
by #1, followed by plain #3).


/martin



Post generated using Mail2Forum (http://m2f.sourceforge.net)
kostis at user.it.uu.se
Posted: Thu Dec 11, 2003 10:43 pm Reply with quote
Guest
I've sent the following at about 3:00 last night, but have not seen
it in the list yet. I am re-sending it again; apologies if you get
it twice.

Kostis.

---------------------------------------------------------------------

Glad to receive so many responses. Let me reply on a few of them.


> On Wed, 10 Dec 2003, Vlad Dumitrescu wrote:
>
> > > test(A) ->
> > > a + 42.
> >
> > For the "A vs a" problem, there is already a warning being issued. Possibly
> > the full warnings options should be enabled by default?

First of all, let's disconnect this thread from "unused_variables"
(although the warning **SHOULD** be turned on by default). Let's
say that the program was:

test(A) ->
bar(A),
a + 42.

> > Since both a and 42 are constants, I feel it would be easy (besides useful)
> > to be able to detect it at compile time. The operation should even be
> > evaluated at compile time, IMHO.
>
> Exactly. And if compile time evaluation fails, give compile time error
> {badarith,....}.

Regardless of whether this is evaluated at compile time or not, there
are still three options as I wrote in my previous mail:

1. the compiler happily compiles this program, but simply replaces
a + 42 with a call to spit {badarith,....}
2. generate a warning, but also byte code.
3. the compiler refuses to compile such erroneous code.

I will try to make a case for 3 below.

Option 1, seems strange to me: The compiler has found out that a particular
evaluation will result in an error, but does not inform its user about it
and moreover generates more code (in terms of space) than that needed for
the expression "a + 42" (if the expression is statically evaluated to
badarith) or refuses to do work at compile time, leaving this work for
run-time (which is what the BEAM compiler currently does).

Option 2 on the surface seems a reasonable choice, but is it really ?
I claim that at least 90% of all such cases are programming errors.
As Taavi mentioned, if the programmer _really_ wants to generate an
error, there is throw() and the a + 42 can be its argument (either
as a string or as a tuple). Seems much, much cleaner to me.

Now let me tell you why I am interested in 3:

In the context of generating native code, it is really a nuisance
to have to handle all sorts of illegal combinations of arguments
to "basic" primitives like "+". Moreover, in the case of HiPE,
because the compiler is (much) more sophisticated than the BEAM
one, it actually discovers quite a lot more cases of such errors.
Currently, we try our best to "immitate" the behaviour of the BEAM
compiler, but in sleepless nights like this one, I keep wondering
whether there should really be a limit to the level of naivety
one pretends to show, or shouldn't there be?

(Just in case it is not obvious, this is a more general issue,
not just related to the simple "a+42" case I chose to illustrate
my point.)

Cheers,
Kostis.

PS. I am curious whether Eric Newhuis could elaborate on:

> But we presently rely on this behavior and do not
> treat it as a warning.

Exactly how do you "rely" on this?



Post generated using Mail2Forum (http://m2f.sourceforge.net)
kent at erix.ericsson.se
Posted: Thu Dec 11, 2003 10:57 pm Reply with quote
Guest
Kostis Sagonas <kostis_at_user.it.uu.se> writes:
> I've sent the following at about 3:00 last night, but have not seen
> it in the list yet. I am re-sending it again; apologies if you get
> it twice.

The delay is because you are not a member of the list using the
address <kostis_at_user.it.uu.se>. I have to manually approve your
postings each time (and today I had no access to my mail). You joined
the list as <kostis_at_csd.uu.se>,

Do you want to change address or use both?

kent


Post generated using Mail2Forum (http://m2f.sourceforge.net)
luke at bluetail.com
Posted: Thu Dec 11, 2003 11:00 pm Reply with quote
Guest
Kostis Sagonas <kostis_at_user.it.uu.se> writes:

> Now let me tell you why I am interested in 3:
>
> In the context of generating native code, it is really a nuisance
> to have to handle all sorts of illegal combinations of arguments
> to "basic" primitives like "+". Moreover, in the case of HiPE,
> because the compiler is (much) more sophisticated than the BEAM
> one, it actually discovers quite a lot more cases of such errors.
> Currently, we try our best to "immitate" the behaviour of the BEAM
> compiler, but in sleepless nights like this one, I keep wondering
> whether there should really be a limit to the level of naivety
> one pretends to show, or shouldn't there be?

Another option would be "2.5". Suppose the function is:

foo(A) ->
X = a+42,
bar(X).

having detected the problem you could compile it as essentially:

foo(A) ->
throw({badarith, a, 42}).

Would that ease the compiler work any?

Some other compilers do exactly this. I know because once, due to a
type-inferencer bug, I got a message like:

Error: 42 is not an integer.

because the compiler had wrongly convinced itself that a variable
could never be bound to an integer, but at runtime when the error was
generated it actually was :-)



Post generated using Mail2Forum (http://m2f.sourceforge.net)
ok at cs.otago.ac.nz
Posted: Thu Dec 11, 2003 11:30 pm Reply with quote
Guest
Concerning

test(A) -> a + 42. /* 1 */

I like to have tools that I can form mental models of.
Suppose we have a compiler that catches the clause above.
Now let's change it:

test(A) -> X = a, X + 42. /* 2 */

Now /* 2 */ _ought_ to have exactly the same semantics as /* 1 */.
But will it? Not if the compiler lets /* 2 */ past but rejects /* 1 */.
To be consistent, the compiler had better do some kind of data flow
analysis. But then we bring in

test(A) -> f(A) + 42. /* 3 */
f(A) -> a.

Apart from tracing, run time, and number of reductions, we'd expect
/* 3 */ to behave just like /* 1 */. Is it proposed that the compiler
will catch this also? Now we're into inter-procedural data flow analysis.

In order to predict which clauses will be rejected and which will be
allowed through, suddenly I have to know more than syntax rules and
variable scope, I have to know quite a bit about how the compiler works.

Now let's try another one. This one is NOT expected to have the same
semantics as /* 1 */, /* 2 */, or /* 3 */.

test(A) -> if 0==0 -> A+42 ; 1==0 -> a+42 end. /* 4 */

Why should the compiler warn about a+42 when there is no possibility of
that expression ever being evaluated?

In order to figure out whether /* 4 */ will be accepted or rejected,
I have to know whether the compiler does its weak type checking before
or after dead code elimination.

A compiler can warn about anything. There's nothing to stop a compiler
saying "Warning: your PC has a camera, you are UGLY." or even
"Warning: code compiled on Friday 13 may not work" or
"Warning: I've seen this before and I didn't like it then either".
But *inconsistent* rejection is another matter entirely.

Consider the following Haskell program as an analogue:

module Main(main)
where
main = print (x `div` x)
x :: Int
x = 0

This is guaranteed to produce an error at run time. Guaranteed.
0 is not a legal right argument for `div`; this is just like the
fact that 'a' is not a legal argument for + .

I tried this program with three different Haskell compilers.
All of them were completely silent about it at compile time.
All of them gave a run time exception.
Yet some of them do quite serious unfolding; at least one of them
_must_ have noticed that I was asking for (0 `div` 0).

My vote would therefore be for option 2:
warn about it but still generate code.


Post generated using Mail2Forum (http://m2f.sourceforge.net)
luke at bluetail.com
Posted: Fri Dec 12, 2003 1:48 am Reply with quote
Guest
Kostis Sagonas <kostis_at_user.it.uu.se> writes:

> 1. the compiler happily compiles this program, but simply replaces
> a + 42 with a call to spit {badarith,....}

(Oops, I see you were way ahead of me already. I was too eager to
share that '42 not integer' anecdote Smile)



Post generated using Mail2Forum (http://m2f.sourceforge.net)
ok at cs.otago.ac.nz
Posted: Fri Dec 12, 2003 2:01 am Reply with quote
Guest
Kostis Sagonas <kostis_at_user.it.uu.se> wrote:
Option 2 on the surface seems a reasonable choice, but is it really ?
I claim that at least 90% of all such cases are programming errors.

And a warning from the compiler tells the programmer about it.

In the context of generating native code, it is really a nuisance
to have to handle all sorts of illegal combinations of arguments
to "basic" primitives like "+".

But choosing option 3 does not remove this requirement.

-module(foo).
-export(bar/1).
bar(X) -> X + 42.

a+42 is *not* the typical case. This is. And it is precisely in this
case that you have no idea what X is going to be.

The following cases are surely legal:
fixnum + fixnum -- add, may overflow and generate bignum
fixnum + bignum -- call out-of-line code
fixnum + flonum -- convert, add, box (lazily)
bignum + fixnum -- call out-of-line code
bignum + bignum -- call out-of-line code
bignum + flonum -- call out-of-line code
flonum + fixnum -- convert, add, box (lazily)
flonum + bignum -- call out-of-line code
flonum + flonum -- add, box (lazily)

By box (lazily) I mean that the compiler may leave the result in a
floating-point register and only box it when/if it is returned or
included in a data structure.

With 9 legal cases, surely handling one more ("none of the above") is
no big deal. There would appear to be the following interesting cases:
(1) The compiler can prove that X and Y are fixnums:
addcc X, Y, result
bpvs,pn %icc,overflow_handler
nop
(2) The compiler can prove that X is a fixnum and Y a flonum:
(get X into an fp register as 32-bit integer; get Y into an fp reg.)
fitod fX, fX
fadd fX, fY, fresult
(3) The compiler can prove that X is a flonum and Y a fixnum:
(get Y into fp reg as 32-bit integer; get X into fp reg.)
fitod fY, fY
fadd fX, fY, fresult
(4) The compiler can prove that X and Y are both flonums
fadd fX, fY, fresult
(5) None of the above
taddcc X, Y, result
bpvc,pt %icc,1f
nop
call general_case
nop
1:
Or, on a machine without tagged add,
or X, Y, result
andcc result, 3, %g0
bpnz,pn 1f
add X, Y, result
bpvc,pt %icc,2f
nop
1: call general_case
nop
2:

This can be simplified a bit. Suppose Y, for example, is a constant
which will bit as an immediate operand. Then

andcc X, 3, %g0
bpnz,pn 1f
add X, immedY, result
bpvc,pt %icc,2f
nop
1: call general_case
set immedY, Y
2:

But it really can't be simplified _much_.

The problem with (1), of course, is that just because the operands
of + are both fixnums doesn't mean the result is; the result could be
a fixnum or a bignum, so when you do X+X+X the second add has to be
the general case (simplified because we don't need the 'or'; we know
X is still fixnum, but we _don't_ know that X+X is).

There are only three ways I can think of to get an integer calculation
down to a single instruction:

(1) Use taddcctv, which traps on overflow, and put the general case in
the trap handler. However, that does nasty things to an Ultra's
pipeline and is now a deprecated instruction. Keeps the code short,
though. This only works for add, subtract, and compare, of course,
and isn't available for most machines.

(2) Use some really amazing interval analysis as well as type inference.
Erlang doesn't give you much to get started with, though. About the
only thing I can think of is that starting with size(_) and counting
down towards 0 should be safe.

(3) Stop supporting Erlang, and implement a related language in which
only fixnums exist, no bignums. (There was some discussion at one
time about having the Erlang standard only require 64-bit integers,
but even those would count as bignums on a 32-bit machine.) Of
course this would cause great problems interoperating with C, Java,
CORBA, and so on (C99, Java, and CORBA all require 64-bit integers,
don't they?), so that won't do.

Moreover, in the case of HiPE,
because the compiler is (much) more sophisticated than the BEAM
one, it actually discovers quite a lot more cases of such errors.

That's great. So issue a lot more warnings. The BEAM and HiPE compilers
should accept *exactly* the same language. (Although HiPE could "accept"
some files in the sense of giving up and leaving them as BEAM.)
But "accept" is not the same as "not warn about".

I keep wondering
whether there should really be a limit to the level of naivety
one pretends to show, or shouldn't there be?

This is an argument against option 1. It is NOT an argument for option 3.
Option 2 (complain loudly but accept, compatibly with BEAM, and generate
code which generates compatible run-time behaviour) is also non-naive.



Post generated using Mail2Forum (http://m2f.sourceforge.net)
bjorn at erix.ericsson.se
Posted: Fri Dec 12, 2003 10:08 am Reply with quote
Guest
Kostis Sagonas <kostis_at_user.it.uu.se> writes:

[...]
> Now let me tell you why I am interested in 3:
>
> In the context of generating native code, it is really a nuisance
> to have to handle all sorts of illegal combinations of arguments
> to "basic" primitives like "+". Moreover, in the case of HiPE,
> because the compiler is (much) more sophisticated than the BEAM
> one, it actually discovers quite a lot more cases of such errors.
> Currently, we try our best to "immitate" the behaviour of the BEAM
> compiler, but in sleepless nights like this one, I keep wondering
> whether there should really be a limit to the level of naivety
> one pretends to show, or shouldn't there be?
>

We in the OTP team are planning for the R10B release to generate more
warnings for suspect code. We will do that in one of the Core Erlang
passes (which is common pass used for both threaded Beam code and native
HiPE code). At the same time as the warning is generated, the offending
code will be replaced with an explicit failure (e.g. erlang:fault/2), that
should be no problem for the later HiPE passes to handle.

Thus, the work only needs to be done once.

As I have already written in a previous answer, we have different opinions
within the Erlang/OTP team at Ericsson whether #2 or #3 is the best solution,
but as someone pointed out, most of work is in detecting the suspect code
anyway. Either #2 or #3 will be implemented in the common parts of the
compiler in R10B.

/Bjorn
--
Bj

Display posts from previous:  

All times are GMT
Page 2 of 6
Goto page Previous  1, 2, 3, 4, 5, 6  Next
This forum is locked: you cannot post, reply to, or edit topics.

Jump to:  

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum