Home     |     .Net Programming    |     cSharp Home    |     Sql Server Home    |     Javascript / Client Side Development     |     Ajax Programming

Ruby on Rails Development     |     Perl Programming     |     C Programming Language     |     C++ Programming     |     IT Jobs

Python Programming Language     |     Laptop Suggestions?    |     TCL Scripting     |     Fortran Programming     |     Scheme Programming Language


 
 
Cervo Technologies
The Right Source to Outsource

MS Dynamics CRM 3.0

Fortran Programming Language

Transfer and variables that don't use all their storage space.


Hello, all!

I'm working on implementing compile-time handling of the transfer
intrinsic for GFortran, and have a couple of edge-case questions about
the standard that I'm hoping someone here can comment on.

The Fortran 2003 standard states that TRANSFER(TRANSFER(E,D),E) should
result in E, if D and E are scalar variables and the physical
representation of D is as long as or longer than that of E.  (Section
13.7.121, lines 30-32.)

Now, consider a processor which supports 4-byte LOGICAL and INTEGER
variables, and suppose that D is a LOGICAL and E is an INTEGER.
Further, suppose that the canonical representations of 4-byte logicals
in this processor are to flip the 1-bit to either 1 or 0, and leave the
rest zero.

A naive reading of the double-transfer requirement would claim that, if
E is some number other than 1 or 0, then the processor is required to
feed that into D's storage space and leave it there, so that it can then
be converted back into the same number by the outer transfer statement.
  In particular, according to this reading, the inner transfer statement
cannot "normalize" the bit representation into one of the canonical
LOGICAL forms, because then the outer transfer statement would return
either 1 or 0, neither of which is equal to E.

I would contend that this reading is incorrect -- that, in particular,
if E has a bit representation that is not one of the canonical bit
representations for a logical variable, then the inner transfer
statement is illegal according to the comments in the beginning of
section 13.7 which state that a program is not allowed to invoke an
intrinsic with arguments that produce out-of-range results.  And further
that the double-transfer identity requirement obviously is only meant to
apply when both transfer calls, taken independently, are legal.

Is that a reasonable contention?

Further, a strictly literal reading of the beginning of section 13.7
would suggest that when transferring a value to a real number, NaN
should be returned if the bit representation is not a legal real number.
  In particular, this might apply for 80-bit real numbers that are
stored in 12 bytes, if the extraneous 12 bytes are non-zero (or whatever
their "usual" state is).  I'm hoping that this is reading things
excessively finely, but I suppose I should ask rather than assuming such.

- Brooks

--
The "bmoses-nospam" address is valid; no unmunging needed.

Brooks Moses <bmoses-nos@cits1.stanford.edu> wrote:
> I'm working on implementing compile-time handling of the transfer
> intrinsic for GFortran, and have a couple of edge-case questions about
> the standard that I'm hoping someone here can comment on.

Edge cases of TRANSFER are a mess. I'm convinced the standard is
inconsistent about some of them.  Anyway...

> The Fortran 2003 standard states that TRANSFER(TRANSFER(E,D),E) should
> result in E, if D and E are scalar variables and the physical
> representation of D is as long as or longer than that of E.  (Section
> 13.7.121, lines 30-32.)

That's one part I'm sure is inconsistent. That can't realistically be
expected to work in all cases. I think that whoever wrote those words
just didn't think of the edge cases at all. Consider a scalar of a type
that has allocatable components (perhaps multiple of them, at multiple
depths). I really don't think that the standard envisions going through
and doing all the allocations that would be needed to make that work;
and I sure don't know what the intermediate bits would be.

That's without even thinking of issues like hardware locations that just
don't allow some bit patterns - fortunately that's not a typical
situation for today's machines.

> Now, consider a processor which supports 4-byte LOGICAL and INTEGER
> variables, and suppose that D is a LOGICAL and E is an INTEGER.
> Further, suppose that the canonical representations of 4-byte logicals
> in this processor are to flip the 1-bit to either 1 or 0, and leave the
> rest zero.

> A naive reading of the double-transfer requirement would claim that, if
> E is some number other than 1 or 0, then the processor is required to
> feed that into D's storage space and leave it there, so that it can then
> be converted back into the same number by the outer transfer statement.
>   In particular, according to this reading, the inner transfer statement
> cannot "normalize" the bit representation into one of the canonical
> LOGICAL forms, because then the outer transfer statement would return
> either 1 or 0, neither of which is equal to E.

I think I agree with that. I'm not sure what is naive about it. It is
pretty explicit that all transfer does is copy bits - not convert them.
The bit that you quoted about transfer(transfer(e,d),e) is more of an
elaboration of what you'd expect (in simple cases) to result from
transfering the bits. I think it belongs as a note instead of as
normative text. (And it probably should be corrected with
qualifications, even as a note.)

> I would contend that this reading is incorrect -- that, in particular,
> if E has a bit representation that is not one of the canonical bit
> representations for a logical variable, then the inner transfer
> statement is illegal according to the comments in the beginning of
> section 13.7 which state that a program is not allowed to invoke an
> intrinsic with arguments that produce out-of-range results.  And further
> that the double-transfer identity requirement obviously is only meant to
> apply when both transfer calls, taken independently, are legal.

I agree, except with your comment that the para before is incorrect.
These two paras don't seem inconsistent to me. I think *BOTH* of them
are right. The first para describes what the standard requires to happen
(that the bits just get copied without change). The second para
correctly (in my view) notes that this requires disallowed behavior in
some cases. I agree with the conclusion that the code is illegal on
machines where the resulting bit pattern is an invalid one.

> Further, a strictly literal reading of the beginning of section 13.7
> would suggest that when transferring a value to a real number, NaN
> should be returned if the bit representation is not a legal real number.

I don't see that. As mentioned above, the essential definition of
TRANSFER is that is just copies the bits. I don't think it ever does any
kind of manipulation of them, such as normalizing, generating NaNs or
anything of the sort. You just get the bits as is. If those bits aren't
valid for any reason, then the code is illegal. Note that assignment can
do things like normalize, so the result of

  somevariable = transfer(something,somevariable)

might end up with somevariable being normalized, even though the
transfer intrinsic didn't do it.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

The internal representation of ALLOCATABLE components
of a derived type would almost certainly be very similar to the
internal representation of a POINTER component.  It would
probably consist of an address field, some fields to describe
the bounds of the allocated object in each of its dimensions,
and some tags specifying status (at least one: whether the object
is allocated or not).  As such, that internal representation
probably takes up the same amount of room whether the
components are allocated or not.  What TRANSFER would
operate on would be the implementation dependent bits of
such a hidden descriptor.  People have used such tricks to
reverse engineer and then manipulate the internal representation
of POINTERs before now.

--
J. Giles

"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies."   --  C. A. R. Hoare

I mean "naive" in the sense of looking just at that paragraph, without
considering context or outside knowledge.  I don't mean that I think
it's necessarily wrong.

I think I'm confused as to what it means, in practical terms, when "the
standard ... requires disallowed behavior".  I'm not sure how to write a
compiler that disappears in a puff of logic in such cases!  :)

Perhaps it would be useful for me to elaborate a bit more on why I'm
asking this question.  GFortran currently, in its handling of
initialization expressions, treats all logical variables as simple
booleans, stored for convenience in whatever the host considers to be a
default integer (which is usually 4 bytes).  However, it supports 8-byte
logical variables.  Thus, consider the following program-piece:

   logical(kind=8) :: L
   integer :: A( -transfer(transfer(-5_8, L), -5_8) )

(Note that, in GFortran, KIND=8 logicals and integers are 8-byte.)

In order to do the initialization-expression handling to reliably
produce a 5-element array out of this, the constant-folder for the inner
transfer evaluation needs to store all eight bytes of the result.

Doing that would either require that the entire initialization-
expression evaluation mechanism store all logical variables in a way
that records all eight bytes of "target storage" even though they are
almost never meaningful, or else the parser has to recognize this
particular idiom and handle it specially.  Neither of these is a very
appealing option, and that's just one of the simpler cases.

However, if this is illegal code, then I would think that it would be
entirely standard-conforming for the compiler to do just about anything
with this code, and thus such measures are unnecessary.

If I understand your above comments correctly, you've agreed that this
is illegal code, but you also explicitly agreed with the second half of
my first paragraph (the bit starting with "In particular," which says
that transfer(transfer(-5_8, L), -5_8) must return -5, not 1.  It seems
completely counterintuitive to me that the compiler is required to
produce a specific result for illegal code.  However, that's what I'm
understanding you to be saying.

Would it be allowable, at least, for a compiler to throw an error and
refuse to compile "transfer(transfer(-5_8, L), -5_8)" at all?

- Brooks

--
The "bmoses-nospam" address is valid; no unmunging needed.

Yes, that's what I would expect. *HOWEVER*, if you transfer the bits of
such a descriptor that is allocated, you are likely to end up with two
different allocated allocatables trying to use the same storage. That is
certainly not envisioned and is likely to cause havoc. A straightforward
reading of the words that Brooks cited above seems to suggest that it
ought to work, since the resulting variable is claimed to have the same
value as the original. That's the kind of situation I'm envisioning, and
I think the cited words of the standard completely fall apart for cases
like that.

Yes, I can imagine what I'd expect to happen in practice, based on
transfer just copying the bits, and I can imagine people trying to take
advantage of that for system-dependent stuff. No debate there. I'm
talking purely about standard-conforming code, and code that the
standard claims ought to be portable as well by the above citation.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

Brooks Moses <bmoses-nos@cits1.stanford.edu> wrote:
> Richard Maine wrote:
> I think I'm confused as to what it means, in practical terms, when "the
> standard ... requires disallowed behavior".

My phrasing of that isn't the world's best (or even second best :-)),
but I was basically agreeing with what seemed to be your logic. If the
standard requires behavior that the standard also disallows, I think one
can deduce that code that does that is non-conforming (by one of those
basic section 1.4 arguments). Thus the compiler can do anything.

> Doing that would either require that the entire initialization-
> expression evaluation mechanism store all logical variables in a way
> that records all eight bytes of "target storage" even though they are
> almost never meaningful,

Ok. I'm the naive one here, but I think that is the behavior anticipated
by the standard. If variables of a type (the 8-byte logical one or any
other) can be used as repositories of arbitrary bits by transfer, then
it seems to me that you need to store all those bits. Perhaps in special
cases, you can deduce that you can optimize storage of some of them
away, but that seems like an optimization to me.

> If I understand your above comments correctly, you've agreed that this
> is illegal code, but you also explicitly agreed with the second half of
> my first paragraph (the bit starting with "In particular," which says
> that transfer(transfer(-5_8, L), -5_8) must return -5, not 1.  It seems
> completely counterintuitive to me that the compiler is required to
> produce a specific result for illegal code.  However, that's what I'm
> understanding you to be saying.

No. I'm using a back door argument to explain why the code is illegal.
More or less a proof by contradiction, which is perhaps why it seems
contradictory. :-) I arise at a contradiction if I assume the code is
standard conforming. The contradiction is that the standard requires a
result that the standard also prohibits. Thus, I conclude that the
assumption of standard conformance is false.

> Would it be allowable, at least, for a compiler to throw an error and
> refuse to compile "transfer(transfer(-5_8, L), -5_8)" at all?

I think you could argue that. But then you might get users who
considered it a poor quality of implementation; that one is harder. I
have trouble imagining why anyone would ever want to do such a strange
thing, but James Buskirk can probably come up with something useful. :-)

I think the more "obvious" thing to do is to use all 8 bytes. But I
don't know what the costs of that are and what the tradoffs might be of
those costs versus the (rare?) usage of an idiom like that.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

There are a lot of things that can cause havoc.  I think the committee
probably envisioned that abuse of TRANSFER would cause havoc.
Everything about TRANSFER is implementation dependent.  So, its
not even possible to discuss the havoc (except speculatively) without
stating what implementation you're talking about.  The semantics of
TRANSFER are defined in terms of *representations* and not
*values*.  The fact that the resulting *values* are meaningless (the
LOGICAL result transferred from INTEGER, for example) or even
dangerous (multiple ALLOCATABLE components associated with
the same storage) is the programmer's responsibility.

Still, I don't see the ALLOCATABLE components problem as all
that bad.  The result of TRANSFER must be assigned or it's lost.
(OK, it could also be an actual argument to a procedure, and maybe
there are other uses I can't think of on an Easter night.  But let's
consider
the obvious first.)  So, suppose you do:

   var = transfer(transfer(E, D), E)

Where E and VAR are both some derived type and D is some type
big enough to hold all the bits of the derived type.  In this case, the
ALLOCATABLE components of VAR do *not* end up associated
with the same storage as the components of E.  This is because of
the semantics of assignment.  It has nothing to do with TRANSFER
at all.  It's the same result as if you had written var = E.  It may be
that the other cases, such as passing the result of TRANSFER as an
actual argument to a procedure, turn out to be equally benign.  You
would have to go through each possibility carefully.  If they aren't
all benign, well that's one of the kinds of havoc.

--
J. Giles

"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies."   --  C. A. R. Hoare

James Giles <jamesgi@worldnet.att.net> wrote:
> Still, I don't see the ALLOCATABLE components problem as all
> that bad.  The result of TRANSFER must be assigned or it's lost.
> (OK, it could also be an actual argument to a procedure, and maybe
> there are other uses I can't think of on an Easter night.  But let's
> consider the obvious first.)

Yes, I did. And I agree that can fix it. I didn't post those details,
but I came to the same conclusion as you did about the likely result of
using assignment. But I claim that the actual argument case can cause
crashes and other "havoc" in code that the standard appears to claim is
standard-conforming and *NOT* processor-dependent. In particular, the
bit about the result having the same value as E is stated as an absolute
- not a processor dependence.

And no, I don't think the authors of the standard consciously thought
about such things. Rather the opposite, I think such matters were
completely overlooked in writing the simplistic words that Brooks cited.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

Richard Maine wrote:

...

> And no, I don't think the authors of the standard consciously thought
> about such things. Rather the opposite, I think such matters were
> completely overlooked in writing the simplistic words that Brooks
> cited.

The point I was making was that I believe the committee deliberately
overlooked such issues.  The purpose of TRANSFER was to provide
a means of doing things that the language otherwise didn't permit.
The fact that such activities could be abused was the programmer's
responsibility.

--
J. Giles

"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies."   --  C. A. R. Hoare

Brooks Moses wrote:

(snip)

> I would contend that this reading is incorrect -- that, in particular,
> if E has a bit representation that is not one of the canonical bit
> representations for a logical variable, then the inner transfer
> statement is illegal according to the comments in the beginning of
> section 13.7 which state that a program is not allowed to invoke an
> intrinsic with arguments that produce out-of-range results.  And further
> that the double-transfer identity requirement obviously is only meant to
> apply when both transfer calls, taken independently, are legal.

In Fortran 66, before CHARACTER variables, the standard allowed
storing of character data, read and written with A format, in
other types of variables.  It was, then, fairly easy to get bit
representations that would not normally be allowed for that data
type, especially for LOGICAL.  In that case, one would have to
be careful how the data was used, but it was at least expected
that assignment to another variable would work.

> Is that a reasonable contention?

In general, I don't believe so.  There are some conditions which I
might wonder about, one is floating point.  On machines with
unnormalized values, assignment might do normalization.

> Further, a strictly literal reading of the beginning of section 13.7
> would suggest that when transferring a value to a real number, NaN
> should be returned if the bit representation is not a legal real number.
>  In particular, this might apply for 80-bit real numbers that are stored
> in 12 bytes, if the extraneous 12 bytes are non-zero (or whatever their
> "usual" state is).  I'm hoping that this is reading things excessively
> finely, but I suppose I should ask rather than assuming such.

Assignment of 12 byte reals could be done by byte copying, instead of
loading into a floating point register and storing from there.

-- glen

"James Giles" <jamesgi@worldnet.att.net> wrote in message

news:welSh.27857$VU4.20719@bgtnsc05-news.ops.worldnet.att.net...

> The point I was making was that I believe the committee deliberately
> overlooked such issues.  The purpose of TRANSFER was to provide
> a means of doing things that the language otherwise didn't permit.
> The fact that such activities could be abused was the programmer's
> responsibility.

Well, it's 21(!) years ago, but my notes on the Halifax meeting in 1986
include:

"Transfer function

My attempts at the previous meeting to restore some form of storage mapping
had failed, but I had been asked to come back with proposals for a transfer
function to allow one area of storage to be viewed as containing more than
one data type. I had prepared two proposals for the meeting but, following
suggestions by John Reid, I rewrote the proposals in the form of a single
function able to return either scalar or array-valued results, depending on
a MOLD argument. This was accepted 20-4. This function would allow us [the
HEP community] to replace code of the form

COMMON//Q(10000)
INTEGER IQ(1)
EQUIVALENCE (IQ,Q)
:
PX = Q(IQ(L) + 3)

by

COMMON//Q(10000)
:
PX = Q(TRANSFER(Q(L), 0) + 3)  ! second argument moulds Q(L) to an
                                                         ! integer scalar"

The only remaining argument was whether the language should *deliberately*
contain an 'unsafe' feature. The functionality was always regarded as taking
two different views of the same storage.

HTH,

Mike Metcalf

James Giles <jamesgi@worldnet.att.net> wrote:
> Richard Maine wrote:
> ...
> > And no, I don't think the authors of the standard consciously thought
> > about such things. Rather the opposite, I think such matters were
> > completely overlooked in writing the simplistic words that Brooks
> > cited.

> The point I was making was that I believe the committee deliberately
> overlooked such issues.  The purpose of TRANSFER was to provide
> a means of doing things that the language otherwise didn't permit.
> The fact that such activities could be abused was the programmer's
> responsibility.

We appear to be miscommunicating. I agree that transfer is to provide a
means of doing such things. That is not my point. Have you reread the
sentence from the standard that Brooks cited? That sentence specifically
says what some cases give as a result, and it says it in a fashion that
is not system dependent. You don't need that sentence to provide
system-dependent capability; quite the opposite - that sentence
disallows system dependence.

It is that sentence and only that sentence that I am arguing about. I
don't see that your argument has anything to do with that sentence. If
you do, then I guess we are just going to have to disagree. I continue
to think that the sentence reflects an inadvertant oversight.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

glen herrmannsfeldt <g@ugcs.caltech.edu> wrote:
> In Fortran 66, before CHARACTER variables, the standard allowed
> storing of character data, read and written with A format, in
> other types of variables.

A more "interesting" case was "statement label values" (aka addresses)
stored in integer variables with the ASSIGN statement. That's
"interesting" because on some architectures they don't "fit". There have
actually been implementations (several of them) where an integer
variable used for such things had two separate addresses of different
sizes, depending on whether it stored an integer value or a statement
label value.

Brooks:

> > Is that a reasonable contention?

> In general, I don't believe so.  There are some conditions which I
> might wonder about, one is floating point.  On machines with
> unnormalized values, assignment might do normalization.

Assignment is an different question. The question asked about the
transfer intrinsic itself - not about assignment of its result. Those
are *NOT* the same thing. A function result can be used without
assigning it.

> Assignment of 12 byte reals could be done by byte copying, instead of
> loading into a floating point register and storing from there.

Again, the question is not about assignment. The distinction matters.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

Richard Maine wrote:
> We appear to be miscommunicating. I agree that transfer is to provide
> a means of doing such things. That is not my point. Have you reread
> the sentence from the standard that Brooks cited? That sentence
> specifically says what some cases give as a result, and it says it in
> a fashion that is not system dependent. You don't need that sentence
> to provide system-dependent capability; quite the opposite - that
> sentence disallows system dependence.

Well, I've thought it over again and I can't see any difference
between passing transfer(transfer(E,D),E)) as an actual argument
and just passing E as the actual argument except the following:
the result of TRANSFER may not be argument associated
with a dummy argument that has OUT among its INTENTs.
I don't see how that difference could cause "havoc".  So, at
least as far as the issue of ALLOCATABLE components,
I still see no problems.

I suppose you can continue to speculate that there *might* be
problems with requiring the double transfer above to be the
identity operation.  But it will remain just that: speculation.
Without a concrete example of such a problem it seems to
me that there are better things to do with one's time.  For
all we know, someone in the F90 committee carefully thought
through all possible cases and found no "havoc" arising from
any of them.  That's speculation too (unless someone remembers
it).  It seems as likely as that there *is* a problem with the
requirement that no one can identify after all these years.

--
J. Giles

"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies."   --  C. A. R. Hoare

James Giles <jamesgi@worldnet.att.net> wrote:
> Richard Maine wrote:
> > That sentence
> > specifically says what some cases give as a result, and it says it in
> > a fashion that is not system dependent.
> Well, I've thought it over again and I can't see any difference
> between passing transfer(transfer(E,D),E)) as an actual argument
> and just passing E as the actual argument except the following:
> the result of TRANSFER may not be argument associated
> with a dummy argument that has OUT among its INTENTs.
> I don't see how that difference could cause "havoc".  So, at
> least as far as the issue of ALLOCATABLE components,
> I still see no problems.

I need to use a temp variable to get the effect. First, I claim that
introducing an intermediate variable cannot change the standard's claim
that transfer((transfer(e,d),e) must return E.  If that made a
difference, then that would mean that the result of transfer was somehow
not captured in its value. In particular, if we change it to

  d = transfer(e,d)
  now use transfer(d,e)

This is for a case where D is of a "benign" type where the assignment
operator doesn't do anything "odd".

So do

  d = transfer(e,d)
  call sub(transfer(d,ee),e)

where ee is another vairiable of the same type as e. I probably don't
even need it, but I use it to remove any question that the first
argument might have any reference to e. Now in the subroutine

  subroutine sub(ee,e)
  ...

I can imagine a fair chance that the allocatable components of e and ee
have managed to get "associated". Well, not as defined by the standard
because it doesn't have any concept like that for allocatables. But if
you change the value of the allocatable component of e, that's likely to
also change the value of the component of ee. Deallocating e could have
other more "interesting" results such as the component of e pointing to
storage that is used for some random other thing. I don't see that this
code violates any of the rules of the standard. In particular, the
actual arguments aren't associated in any way acknowledged by the
standard, so the prohibitions against changing the values of the dummies
don't apply. You can't directly change the value of the first dummy
because its actual is an expression, but you can change the value of the
second.

That's just a pretty much off-the-top-of-my head example. It is slightly
indirect because I did keep running into bars (some of the same ones you
mentioned), but I methodically worked around each one.

Once you have a single "hole" like this, you can open up many others.

> For
> all we know, someone in the F90 committee carefully thought
> through all possible cases and found no "havoc" arising from
> any of them.

You have *GOT* to be kidding. :-) Yes, I saw that you labelled it as
speculation. But I don't even think it is very plausible speculation.
I've seentoo much of the committee close up; I don't think they would
have thought through all the cases of that one, particularly as...

> That's speculation too (unless someone remembers
> it).  It seems as likely as that there *is* a problem with the
> requirement that no one can identify after all these years.

"All these years" isn't very many for allocatable components, which are
where I can most readily see a related problem. Wouldn't surprise me if
there are others, but that's one I think I can see concretely. There's
no way that whoever wrote the transfer description for f90 could have
carefully considered how it worked with allocatable components, the
details of which weren't done til a decade later. I was there during all
of the work on allocatable components, and I don't recall any such topic
relating to transfer ever having been broached. I do recall that
allocatable components had lots and lots of subtleties that took a long
time to work out, both in the standard and in implementations. They seem
like such a simple and "obvious" thing that many people don't see why
they took so long. There was a time when I didn't appreciate the issues.
My first attempts at f90 code, before I had a compiler to check me, used
allocatable components because I just assumed that, of course, you could
do that since you had allocatables and you had components.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

Richard Maine <nos@see.signature> wrote:
> James Giles <jamesgi@worldnet.att.net> wrote:
> > So, at
> > least as far as the issue of ALLOCATABLE components,
> > I still see no problems [with transfer].

> [I gave an example of a problem I see with allocatable components]

But I neglected to mention a related actual problem I once had. It
wasn't with allocatable components, being long before they were valid.
It was instead with pointer components. Pointer components aren't
necessarily as "bad" for this. One could imagine all as working just
fine. It is normal for multiple pointers to have the same target and
there are rules for how they interact. But still, I have had related
problems.

In particular, I have had transfer of pointer components cause problems
with garbage collection. I don't do that kind of thing any more. My
experiences with it were too painful. It is also some of the stuff that
I can see that the f2003 object oriented features would help make
cleaner. For the interim, I used other methods, that don't seem as
elegant or flexible, but did the job for my application. Anyway...

I had a derived type with a pointer component. Actualy, multiple
different derived types, each with different pointer components. It is
determined at run-time which type is being used. In order to fake what I
know know of as polymorphism (though I hadn't heard of the term then), I
used transfer to store the bits in storage of a "neutral" type (I used
an array of integers). The higher level code didn't then need to know
the details of the different lower-level types. The bits were later
transferred back into the appropriate type when it was needed to use
them. But... sometimes the compiler's garbage collection would run while
the bits were stored in the "neutral" type. The pointer targets looked
like allocated memory that had no current pointers to it. So it got
garbage collected. When I later transfered the bits back to the right
type, the targets were gone.

In some senses, I'm not sure this is as good an example of problems as
the allocatable one. But this is one that I actually got bit (um, pun
not intended, or even noticed until I reread this) by in the early 90's.
It is what made me start thinking about the possibility of cases where
just naively copying the bits might not have the effect of copying all
the information correctly. One example that occurred to me (but I don't
think I've seen) would be relative addressing of pointers. If you have
relative addressing, then copying the same bits to another location
doesn't preserve their meaning.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

Richard Maine wrote:

...

> I need to use a temp variable to get the effect. First, I claim that
> introducing an intermediate variable cannot change the standard's
> claim that transfer((transfer(e,d),e) must return E.  If that made a
> difference, then that would mean that the result of transfer was
> somehow not captured in its value. [...

What's "not captured in its value" mean?  TRANSFER operates
on representations, not values.

> ...]                                 In particular, if we change it to

>   d = transfer(e,d)
>   now use transfer(d,e)

Assignments operate on values, not internal representations.
Unless the rules of assignment are *required* to preserve the
internal representation, there's no reason to expect this to work.
For many types, like INTEGER for example, there's no reason
that assignment wouldn't preserve internal representation as well
as value (there's usually only one representation of any particular
value).  But, the standard doesn't *require* the internal representation
to be preserved.  The description of TRANSFER doesn't claim that
an intermediate assignment would preserve the identity in question.
It doesn't mention assignments at all.

...

> This is for a case where D is of a "benign" type where the assignment
> operator doesn't do anything "odd".

> So do

>   d = transfer(e,d)
>   call sub(transfer(d,ee),e)

> where ee is another vairiable of the same type as e. I probably don't
> even need it, but I use it to remove any question that the first
> argument might have any reference to e. Now in the subroutine

>   subroutine sub(ee,e)

Let's rephrase this.  Suppose P and Q are pointers to the type of
E.  And suppose somewhere in the program you wrote:

   P => E

And suppose elsewhere you write:

   Q => P

and the elsewhere you write

   call sub(Q, E)

If SUB doesn't define, redefine, or cause to become undefined either
of its arguments, this is still safe.  Otherwise you're in violation of
the aliasing rule.  But that's the same rule you'd violate with your use
of TRANSFER.  So, it's not TRANSFER the introduces any potential
difficulty you see here, it's our old nemesis aliasing.  The fact that
you've gone through contortions to prevent the compiler from actually
detecting the aliased value doesn't change that.

> [...]        In particular, the actual arguments
> aren't associated in any way acknowledged by the standard, so the
> prohibitions against changing the values of the dummies don't apply.

Now you've *got* to be kidding!  Under your hypothesis (that the
intervening assignment doesn't change internal representations) they
both refer to the same underlying entities.  The rule about aliasing
don't say anything about the entities being associated in *acknowledged*
ways!  If you change the entity, you can only reference the entity through
that one dummy argument.  That's regardless of how the entities are
associated.  The word "associated" isn't even used!

>> For
>> all we know, someone in the F90 committee carefully thought
>> through all possible cases and found no "havoc" arising from
>> any of them.

> You have *GOT* to be kidding. :-) Yes, I saw that you labelled it as
> speculation. But I don't even think it is very plausible speculation.

Well, given the shallow analysis so far in this thread, it's certainly
*very* plausible that F8x people pursued it much further that we
have.  There were lots more of them and they tended to be more
zealously suspicious than the current committee seems to be.

> I've seentoo much of the committee close up; I don't think they would
> have thought through all the cases of that one, particularly as...

>> That's speculation too (unless someone remembers
>> it).  It seems as likely as that there *is* a problem with the
>> requirement that no one can identify after all these years.

> "All these years" isn't very many for allocatable components, which
> are where I can most readily see a related problem. Wouldn't surprise
> me if there are others, but that's one I think I can see concretely.

I still don't see any problem.  You have done enough hand wringing
that there *might* be a problem.  But no *new* problem has been
evident (only ones due to other violations of the standard not related
to the use of TRANSFER).  Your example still does nothing different
than CALL SUB(E, E) would do in the same circumstances.  Your
attempt to characterize it as an unacknowledged form of association
doesn't change anything.

--
J. Giles

"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies."   --  C. A. R. Hoare

Richard Maine wrote:
> [...]     then copying the same bits
> to another location doesn't preserve their meaning.

Exactly the point I made last time: TRANSFER deals with
internal represenatations while assignment (and most other
features of the language) deal with values.  There's no
requirement that operations on values must preserve
representations.

Pointers and garbage collect are a good case in point.
Another reason that your assumption that an intermediate
assignment should be considered fair use of the TRANSFER
identity we've been talking about is just wrong.

--
J. Giles

"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies."   --  C. A. R. Hoare

Richard Maine wrote:

(snip)

> In some senses, I'm not sure this is as good an example of problems as
> the allocatable one. But this is one that I actually got bit (um, pun
> not intended, or even noticed until I reread this) by in the early 90's.
> It is what made me start thinking about the possibility of cases where
> just naively copying the bits might not have the effect of copying all
> the information correctly. One example that occurred to me (but I don't
> think I've seen) would be relative addressing of pointers. If you have
> relative addressing, then copying the same bits to another location
> doesn't preserve their meaning.

There is an old trick of to save memory for doubly linked lists,
I believe sometimes used in C programs.  Instead of storing the
two pointers (next and previous list element) you instead store
the exclusive OR of the two.  In traversing the list, you know where
you are coming from, exclusive OR that with the stored value gives
the pointer to the next element.   I would probably use the difference
instead, but then you need to know which direction you are going.

Neither are legal in standard C.  Pointer comparison and subtraction
are only allowed on pointers to the same object, such as different
array element.  A pointer difference chain would be legal within an
array, but not for separately allocated elements.

Assuming relative pointers are legal Fortran, it would seem to
exclude bit copying.

-- glen

Richard Maine wrote:

(snip)

> "All these years" isn't very many for allocatable components, which are
> where I can most readily see a related problem. Wouldn't surprise me if
> there are others, but that's one I think I can see concretely. There's
> no way that whoever wrote the transfer description for f90 could have
> carefully considered how it worked with allocatable components, the
> details of which weren't done til a decade later.

Some of the subtleties should be similar to those for pointers.
Not all, I agree, but if the effect can be seen for pointer
components it shouldn't be attributed to allocatable components.

 > I was there during all

> of the work on allocatable components, and I don't recall any such topic
> relating to transfer ever having been broached. I do recall that
> allocatable components had lots and lots of subtleties that took a long
> time to work out, both in the standard and in implementations.

-- glen

Richard Maine wrote:
> Brooks Moses <bmoses-nos@cits1.stanford.edu> wrote:

>> Richard Maine wrote:
[...]
>> Would it be allowable, at least, for a compiler to throw an error and
>> refuse to compile "transfer(transfer(-5_8, L), -5_8)" at all?

> I think you could argue that. But then you might get users who
> considered it a poor quality of implementation; that one is harder. I
> have trouble imagining why anyone would ever want to do such a strange
> thing, but James Buskirk can probably come up with something useful. :-)

I'd be one of those who thought is was poor quality of implementation!

I expect TRANSFER to very much be a replacement for EQUIVALENCE,
similar to the remarks of Mike Metcalf in this thread.  It is
perfectly standard-conforming to equivalence integer, logical, real
and complex entities.  Now the standard says that assigning to
one member of an equivalence list makes reference to any of the others
be undefined, IIRC.  But it doesn't say anything about zero-ing out
"unused" bits in the logical member after assignment to the real or
integer member, etc.

I most emphatically agree with other comments in this thread that
conversions done by assignment should NOT be done by TRANSFER.  That
would sort of defeat it's whole raison d'etre, wouldn't it?

    -Ken
--
Ken & Ann Fairfield
What:  Ken dot And dot Ann
Where: Gmail dot Com

> I'd be one of those who thought is was poor quality of implementation!

Then, the user bears the responsability to do only legitimate things, for
a definition of "legitimate" that is not documented by the standard.

Do you agree with this consideration, that you could transfer a value
into a logical variable L such that when calling the following code:

subroutine foo(L)
  logical, intent(in) :: L
  if (L) print *, "Test1"
  if (.not. L) print *, "Test2"
end subroutine foo

could print neither Test1 nor Test2? (ie none of the IF is triggered)

--
FX

FX wrote:
>> I'd be one of those who thought is was poor quality of
>> implementation!

> Then, the user bears the responsability to do only legitimate things,
> for a definition of "legitimate" that is not documented by the
> standard.

Yes, that seems to me the whole purpose of TRANSFER.

--
J. Giles

"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies."   --  C. A. R. Hoare

glen herrmannsfeldt <g@ugcs.caltech.edu> wrote:
> Richard Maine wrote:
> (snip)

> > "All these years" isn't very many for allocatable components, which are
> > where I can most readily see a related problem. Wouldn't surprise me if
> > there are others, but that's one I think I can see concretely. There's
> > no way that whoever wrote the transfer description for f90 could have
> > carefully considered how it worked with allocatable components, the
> > details of which weren't done til a decade later.

> Some of the subtleties should be similar to those for pointers.
> Not all, I agree, but if the effect can be seen for pointer
> components it shouldn't be attributed to allocatable components.

The ones that kept allocatable components from getting into f95, that
required corrections in the TR, and that have made it take a long time
for vendors to actually get allocatable components working are not in
common with pointers. That is to say, pretty much all the points that
I'd count as particularly subtle. I've been using pointer components
reliably for a decade and a half now. I don't have any production code
at all that uses allocatable components because last time I checked,
many of the compilers still had bugs in that area.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

Yes, I agree (although I would probably avoid a system that
behaved that way).

    -Ken
--
Ken & Ann Fairfield
What:  Ken dot And dot Ann
Where: Gmail dot Com

Add to del.icio.us | Digg this | Stumble it | Powered by Megasolutions Inc