|
|
 |
 |
 |
 |
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
Richard Maine wrote: > Brooks Moses <bmoses-nos @cits1.stanford.edu> wrote: ... >> 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.
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
Richard Maine wrote: > Brooks Moses <bmoses-nos @cits1.stanford.edu> wrote: >> 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 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 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.
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.
James Giles <jamesgi @worldnet.att.net> wrote: > Richard Maine wrote: > > Brooks Moses <bmoses-nos @cits1.stanford.edu> wrote: > ... > >> 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. > The internal representation of ALLOCATABLE components > of a derived type would almost certainly be very similar to the > internal representation of a POINTER component.... > What TRANSFER would > operate on would be the implementation dependent bits of > such a hidden descriptor.
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
Richard Maine wrote: > James Giles <jamesgi @worldnet.att.net> wrote: ... >> The internal representation of ALLOCATABLE components >> of a derived type would almost certainly be very similar to the >> internal representation of a POINTER component.... >> What TRANSFER would >> operate on would be the implementation dependent bits of >> such a hidden descriptor. > 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.
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
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. > 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)
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
|
 |
 |
 |
 |
|