* A hole in Ada type safety @ 2011-04-30 8:41 Florian Weimer 2011-04-30 11:56 ` Robert A Duff 0 siblings, 1 reply; 31+ messages in thread From: Florian Weimer @ 2011-04-30 8:41 UTC (permalink / raw) I don't know if this is a new observation---I couldn't find documentation for it. I plan to incorporate feedback into <http://www.enyo.de/fw/notes/ada-type-safety.html>. The standard way to show that type safety has been broken in a language is to implement a cast function Conversion from any type Source to any type Target: generic type Source is private; type Target is private; function Conversion (S : Source) return Target; This generic function can be used like Ada.Unchecked_Conversion: with Ada.Text_IO; with Ada.Unchecked_Conversion; with Conversion; procedure Convert_Test is type Integer_Access is access all Integer; J : aliased Integer; J_Access : Integer_Access := J'Access; function Convert is new Conversion (Integer_Access, Integer); function Unchecked_Convert is new Ada.Unchecked_Conversion (Integer_Access, Integer); begin Ada.Text_IO.Put_Line (Integer'Image (Convert (J_Access))); Ada.Text_IO.Put_Line (Integer'Image (Unchecked_Convert (J_Access))); end Convert_Test; How can we implement Conversion? It turns out that discriminant records with defaults combined with aliasing do the trick: function Conversion (S : Source) return Target is type Source_Wrapper is tagged record S : Source; end record; type Target_Wrapper is tagged record T : Target; end record; type Selector is (Source_Field, Target_Field); type Magic (Sel : Selector := Target_Field) is record case Sel is when Source_Field => S : Source_Wrapper; when Target_Field => T : Target_Wrapper; end case; end record; M : Magic; function Convert (T : Target_Wrapper) return Target is begin M := (Sel => Source_Field, S => (S => S)); return T.T; end Convert; begin return Convert (M.T); end Conversion; When the inner function Convert is called, the discriminant Sel of M has the value Target_Field, thus the component M.T can be dereferenced. The assignment statement in Convert changes the discriminant and the value. But the source value S is still reachable as an object of type Target because the parameter T aliases the component M.T, so the return statement executes without raising an exception. The two tagged records are used to force that the inner Convert function receives its parameter by reference. Without them, an implementation would be free to pass the discriminant record Magic by copy, and aliasing would not occur. Our implementation lacks the full power of Ada.Unchecked_Conversion because it does not supported limited or unconstrained types. However, it is sufficient to break type safety. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-04-30 8:41 A hole in Ada type safety Florian Weimer @ 2011-04-30 11:56 ` Robert A Duff 2011-04-30 15:27 ` Gavino 2011-04-30 16:16 ` Florian Weimer 0 siblings, 2 replies; 31+ messages in thread From: Robert A Duff @ 2011-04-30 11:56 UTC (permalink / raw) Florian Weimer <fw@deneb.enyo.de> writes: > I don't know if this is a new observation---I couldn't find > documentation for it. It's not new. It is documented in AARM-3.7.2(4,4.a), which dates back to Ada 83 days. > When the inner function Convert is called, the discriminant Sel of M > has the value Target_Field, thus the component M.T can be > dereferenced. The assignment statement in Convert changes the > discriminant and the value. But the source value S is still reachable > as an object of type Target because the parameter T aliases the > component M.T, so the return statement executes without raising an > exception. Well, the program exhibits erroneous (i.e. unpredictable) behavior, so anything could happen. The above description is likely to happen. > Our implementation lacks the full power of Ada.Unchecked_Conversion > because it does not supported limited or unconstrained types. However, > it is sufficient to break type safety. Yes. Anything that is erroneous necessarily breaks type safety. If you look up "erroneous execution" in the index, you'll find them all. Your comment, "This note shows that a combination of safe-looking language features can be used to undermine type safety, too." is the key point. It is indeed unfortunate when "safe-looking" features can be erroneous. - Bob ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-04-30 11:56 ` Robert A Duff @ 2011-04-30 15:27 ` Gavino 2011-04-30 16:16 ` Florian Weimer 1 sibling, 0 replies; 31+ messages in thread From: Gavino @ 2011-04-30 15:27 UTC (permalink / raw) "Robert A Duff" <bobduff@shell01.TheWorld.com> wrote in message news:wccoc3oq7ev.fsf@shell01.TheWorld.com... > It is indeed unfortunate when "safe-looking" > features can be erroneous. Hence the need for something like SPARK. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-04-30 11:56 ` Robert A Duff 2011-04-30 15:27 ` Gavino @ 2011-04-30 16:16 ` Florian Weimer 2011-04-30 23:39 ` Randy Brukardt 2011-05-14 23:47 ` anon 1 sibling, 2 replies; 31+ messages in thread From: Florian Weimer @ 2011-04-30 16:16 UTC (permalink / raw) * Robert A. Duff: > Florian Weimer <fw@deneb.enyo.de> writes: > >> I don't know if this is a new observation---I couldn't find >> documentation for it. > > It's not new. It is documented in AARM-3.7.2(4,4.a), > which dates back to Ada 83 days. Ah. I didn't realize that call to Convert was already erroneous. It does not seem possible to extend the restrictions on 'Access prefixes to to subprogram parameters (due to the way controlled types are implemented, for example). >> Our implementation lacks the full power of Ada.Unchecked_Conversion >> because it does not supported limited or unconstrained types. However, >> it is sufficient to break type safety. > > Yes. Anything that is erroneous necessarily breaks type safety. > If you look up "erroneous execution" in the index, you'll find > them all. My concern was that this was not explicitly labeled as erroneous. 8-) > Your comment, "This note shows that a combination of safe-looking > language features can be used to undermine type safety, too." > is the key point. It is indeed unfortunate when "safe-looking" > features can be erroneous. And once there is something like this in the language, it is difficult to decide if a new addition (such as aliased parameters) make things worse or not. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-04-30 16:16 ` Florian Weimer @ 2011-04-30 23:39 ` Randy Brukardt 2011-05-01 10:26 ` Florian Weimer 2011-05-14 23:47 ` anon 1 sibling, 1 reply; 31+ messages in thread From: Randy Brukardt @ 2011-04-30 23:39 UTC (permalink / raw) "Florian Weimer" <fw@deneb.enyo.de> wrote in message news:87tydfbtp3.fsf@mid.deneb.enyo.de... ... > And once there is something like this in the language, it is difficult > to decide if a new addition (such as aliased parameters) make things > worse or not. Not sure how something that adds new capabilities only for elementary types could make anything worse. We also ran across this particular erroneousness in another case; we decided that we had to let subprograms trust their constraints (which introduces some additional erroneousness) as the alternative requires a lot of additional code for discriminanted parameters. (I forget where the wording change for that ended up.) Cases like this is why I like to say that all real Ada programs are erroneous, so any result is allowed. :-) If you could write an entire program in SPARK, that could make that statement a falsehood, but the Ada runtime is most likely erroneous for some reason (interfacing to C tends to end up erroneous in some cases, for instance). You'd probably need a SPARK runtime on top of a SPARK OS in order to make a non-erroneous program a reality. Randy. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-04-30 23:39 ` Randy Brukardt @ 2011-05-01 10:26 ` Florian Weimer 2011-05-03 1:40 ` Randy Brukardt 0 siblings, 1 reply; 31+ messages in thread From: Florian Weimer @ 2011-05-01 10:26 UTC (permalink / raw) * Randy Brukardt: > "Florian Weimer" <fw@deneb.enyo.de> wrote in message > news:87tydfbtp3.fsf@mid.deneb.enyo.de... > ... >> And once there is something like this in the language, it is difficult >> to decide if a new addition (such as aliased parameters) make things >> worse or not. > > Not sure how something that adds new capabilities only for > elementary types could make anything worse. It introduces a new form of aliasing. This will be problematic, I fear. In particular, extreme care is necessary so that the cop-out in 3.7.2(4) is still the only one which is needed. With this rule (I don't know if this is the current text): | If the formal parameter is an explicitly aliased parameter, the type | of the actual parameter shall be tagged or the actual parameter | shall be an aliased view of an object. it is possible to obtain an aliased view of a discriminant-dependent record component of tagged type which is not delimited by a subprogram call. This would allow to write the Conversion function like this: function Conversion (S : Source) return Target is type Source_Wrapper is tagged record S : Source; end record; type Target_Wrapper is tagged record T : Target; end record; type Selector is (Source_Field, Target_Field); type Magic (Sel : Selector := Target_Field) is record case Sel is when Source_Field => S : Source_Wrapper; when Target_Field => T : Target_Wrapper; end case; end record; M : Magic; function Convert (T : aliased Target_Wrapper) return access constant Target is begin return T.T'Access; end Convert; T : Target renames Convert (M.T).all; begin M := (Sel => Source_Field, S => (S => S)); return T; end Conversion; (I haven't checked with an Ada 2012 compiler, so this is somewhat speculative.) It seems to me that you need a C++-like rule that accessing an object through a view which is not compatible with its dynamic type is erroneous. And this would be quite a defeat. The difficulty I alluded lies in the answer to this question: Are aliased parameters at fault, or are discriminants with defaults to blame? > Cases like this is why I like to say that all real Ada programs are > erroneous, so any result is allowed. :-) It's a bit disappointing because all those nitty-gritty rules intended to ensure type safety which make the language quite complicated, and there's still such a hole. It's certainly at odds with the marketing materials. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-01 10:26 ` Florian Weimer @ 2011-05-03 1:40 ` Randy Brukardt 2011-05-03 16:57 ` Robert A Duff 2011-05-07 9:09 ` Florian Weimer 0 siblings, 2 replies; 31+ messages in thread From: Randy Brukardt @ 2011-05-03 1:40 UTC (permalink / raw) "Florian Weimer" <fw@deneb.enyo.de> wrote in message news:87d3k2u36e.fsf@mid.deneb.enyo.de... >* Randy Brukardt: >> "Florian Weimer" <fw@deneb.enyo.de> wrote in message >> news:87tydfbtp3.fsf@mid.deneb.enyo.de... >> ... >>> And once there is something like this in the language, it is difficult >>> to decide if a new addition (such as aliased parameters) make things >>> worse or not. >> >> Not sure how something that adds new capabilities only for >> elementary types could make anything worse. > > It introduces a new form of aliasing. This will be problematic, I > fear. In particular, extreme care is necessary so that the cop-out in > 3.7.2(4) is still the only one which is needed. With this rule (I > don't know if this is the current text): > > | If the formal parameter is an explicitly aliased parameter, the type > | of the actual parameter shall be tagged or the actual parameter > | shall be an aliased view of an object. > > it is possible to obtain an aliased view of a discriminant-dependent > record component of tagged type which is not delimited by a subprogram > call. But this is not a new capability. All tagged type parameters are implicitly aliased already in Ada (Ada 95 specifically), so there is nothing new being added here. (And in all honesty, all tagged objects should be implicitly aliased; it is a mistake to require that declaration on tagged types.) > This would allow to write the Conversion function like this: > > function Conversion (S : Source) return Target is > type Source_Wrapper is tagged record > S : Source; > end record; > type Target_Wrapper is tagged record > T : Target; > end record; > > type Selector is (Source_Field, Target_Field); > type Magic (Sel : Selector := Target_Field) is record > case Sel is > when Source_Field => > S : Source_Wrapper; > when Target_Field => > T : Target_Wrapper; > end case; > end record; > > M : Magic; > > function Convert > (T : aliased Target_Wrapper) > return access constant Target > is > begin > return T.T'Access; > end Convert; > > T : Target renames Convert (M.T).all; > > begin > M := (Sel => Source_Field, S => (S => S)); > return T; > end Conversion; > > (I haven't checked with an Ada 2012 compiler, so this is somewhat > speculative.) Actually, I think T.T'Access is illegal, because the component T is not declared as aliased. You can take 'Access of the parameter T, but not of some random component (as is currently the case in Ada). You could probably do it by adding an additional level of subprogram calls (that is, by passing T.T to another function that returns the access value). But I think you can already do that in Ada 95 (perhaps you would have to use 'Unchecked_Access there). But this looks like an attempt to shot yourself in the foot; it's hardly likely to happen by accident. Remember that no programming language can really defend against malicious programmers; the point is to defend against sloppy or careless programmers [which is all of us, sooner or later]. For instance, the tampering checks of the Ada containers implicit in the new Reference routines can be defeated by the use of Unchecked_Deallocation on the return object. But this is not something that is reasonably going to happen by accident, and it is out in the open for program reviewers (both tools and humans) to find. It's not worth worrying about. > It seems to me that you need a C++-like rule that accessing an object > through a view which is not compatible with its dynamic type is > erroneous. And this would be quite a defeat. But that's exactly what 3.7.2(4) is. And it has been in Ada since the beginning. It's not a real problem in practice because the use of constrained discriminated types is pretty rare. Also note that a lot of renaming and 'Access of discriminant-dependent components is not allowed; we've tighened those rules with each version of Ada. > The difficulty I alluded lies in the answer to this question: Are > aliased parameters at fault, or are discriminants with defaults to > blame? Almost certainly discriminants with defaults, since the problem has existed in Ada since Ada 83, and we are not making it any worse (that would be impossible, IMHO). >> Cases like this is why I like to say that all real Ada programs are >> erroneous, so any result is allowed. :-) > > It's a bit disappointing because all those nitty-gritty rules intended > to ensure type safety which make the language quite complicated, and > there's still such a hole. It's certainly at odds with the marketing > materials. Type safety is not about detering malicious programmers, merely careless ones. And what you are doing is clearly malicious, since its intent is to hide an Unchecked_Conversion operation in some other, far more complex form. Unless this is likely to happen by accident (and it is not), there isn't really a problem. We actually considered repealing that 3.7.2(4) for Ada 2012, but decided not to simply because it would be very hard on programmers (you could never trust a composite constraint). Programmers *want* to trust constraints, even though they aren't anywhere near as meaningful as most think. (We had a replay of this with the predicate feature; there we decided to separate the "meaningful" ones from the other kind.) Randy. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-03 1:40 ` Randy Brukardt @ 2011-05-03 16:57 ` Robert A Duff 2011-05-07 9:09 ` Florian Weimer 1 sibling, 0 replies; 31+ messages in thread From: Robert A Duff @ 2011-05-03 16:57 UTC (permalink / raw) "Randy Brukardt" <randy@rrsoftware.com> writes: > It's not a real problem in practice because the use of constrained > discriminated types is pretty rare. And passing an actual parameter to a procedure that can see that actual as a global is also pretty rare. - Bob ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-03 1:40 ` Randy Brukardt 2011-05-03 16:57 ` Robert A Duff @ 2011-05-07 9:09 ` Florian Weimer 2011-05-07 9:28 ` Dmitry A. Kazakov 2011-05-09 20:41 ` Randy Brukardt 1 sibling, 2 replies; 31+ messages in thread From: Florian Weimer @ 2011-05-07 9:09 UTC (permalink / raw) * Randy Brukardt: > Actually, I think T.T'Access is illegal, because the component T is not > declared as aliased. You can take 'Access of the parameter T, but not of > some random component (as is currently the case in Ada). Wouldn't it work if the component T was aliased? > But this looks like an attempt to shot yourself in the foot; it's hardly > likely to happen by accident. Remember that no programming language can > really defend against malicious programmers; the point is to defend against > sloppy or careless programmers [which is all of us, sooner or later]. My first answer that was rather confrontational. Let me just say that this is the line of argument used by C advocates. > For instance, the tampering checks of the Ada containers implicit in > the new Reference routines can be defeated by the use of > Unchecked_Deallocation on the return object. But this is not > something that is reasonably going to happen by accident, and it is > out in the open for program reviewers (both tools and humans) to > find. It's not worth worrying about. It's true that use-after-free errors typically result in exactly the same type safety violation. But they seem to be rather hard to spot in COTS software, and I'm not sure that the programming languages are to blame. Manual memory management is just hard. >> It seems to me that you need a C++-like rule that accessing an object >> through a view which is not compatible with its dynamic type is >> erroneous. And this would be quite a defeat. > > But that's exactly what 3.7.2(4) is. And it has been in Ada since the > beginning. 3.7.2(4) has very precise syntact requirements ("a name denoting a subcomponent that depends on discriminants" etc.). I don't think it covers the new possibilities introduced by aliased parameters. > Also note that a lot of renaming and 'Access of discriminant-dependent > components is not allowed; we've tighened those rules with each version of > Ada. It's not clear to me if these rules are valuable when its easy to circumvent them. They mostly seem to increase the complexity of the language. >> The difficulty I alluded lies in the answer to this question: Are >> aliased parameters at fault, or are discriminants with defaults to >> blame? > > Almost certainly discriminants with defaults, since the problem has > existed in Ada since Ada 83, and we are not making it any worse > (that would be impossible, IMHO). I tend to agree. Would it make sense to deprecate discriminants with defaults? As a replacement, smart pointers could be added to the language, with syntactical overhead compared to access types, which would allow programmers to replace discriminants with default with a smart pointer to an unconstrained type. Implementations would be allowed to use reference counting, or full garbage collection. However, they would have to ensure that references to the object held by the smart pointer would remain live for the time any reference to it or its components exists (an actual subprogram parameter, for instance). There would be no promise that cycles do not result in memory leaks. Run-time complexity for reference counting is less than for full garbage collection. Making it a language feature, rather than a library-provided functionality, makes it easier for implementations to optimize updating the counts. As usual, there is a trade-off between flexibility and overhead: if the reference counters are kept separate, it is possible to implement weak pointers and safe references to sub-components. Intrusive counts provide better cache locality and the smart pointers themselves can be a single machine word. > Type safety is not about detering malicious programmers, merely careless > ones. I think type safety is important for ending up with language semantics that can be explained. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-07 9:09 ` Florian Weimer @ 2011-05-07 9:28 ` Dmitry A. Kazakov 2011-05-07 9:57 ` Florian Weimer 2011-05-09 20:41 ` Randy Brukardt 1 sibling, 1 reply; 31+ messages in thread From: Dmitry A. Kazakov @ 2011-05-07 9:28 UTC (permalink / raw) On Sat, 07 May 2011 11:09:44 +0200, Florian Weimer wrote: > Run-time complexity for reference counting is less than for full > garbage collection. Making it a language feature, rather than a > library-provided functionality, makes it easier for implementations to > optimize updating the counts. Hmm, what about tasking? I gather that a built-in counter must be atomic. Whether implemented lock-free or using a spin lock, that would be a considerable overhead. > As usual, there is a trade-off between > flexibility and overhead: if the reference counters are kept separate, > it is possible to implement weak pointers and safe references to > sub-components. Interesting. Do you have a certain schema in mind, how to this? >> Type safety is not about detering malicious programmers, merely careless >> ones. > > I think type safety is important for ending up with language semantics > that can be explained. I agree. It is the semantics which yields to safety, not the reverse. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-07 9:28 ` Dmitry A. Kazakov @ 2011-05-07 9:57 ` Florian Weimer 2011-05-08 8:08 ` Dmitry A. Kazakov 0 siblings, 1 reply; 31+ messages in thread From: Florian Weimer @ 2011-05-07 9:57 UTC (permalink / raw) * Dmitry A. Kazakov: > On Sat, 07 May 2011 11:09:44 +0200, Florian Weimer wrote: > >> Run-time complexity for reference counting is less than for full >> garbage collection. Making it a language feature, rather than a >> library-provided functionality, makes it easier for implementations to >> optimize updating the counts. > > Hmm, what about tasking? I gather that a built-in counter must be atomic. > Whether implemented lock-free or using a spin lock, that would be a > considerable overhead. Modern architectures eliminate most of the overhead in the non-shared case. In the M and E states of the MESI protocol, the operation can be performed locally. There are techniques to emulate this in software, but they require safepointing support, which in turn needs extensive run-time facilities. (A safepoint allows you to carry out in a safe way an operation which is clearly unsafe in the presence of multiple tasks.) Hotspot performs a similar optimization, called biased locking. >> As usual, there is a trade-off between flexibility and overhead: if >> the reference counters are kept separate, it is possible to >> implement weak pointers and safe references to sub-components. > > Interesting. Do you have a certain schema in mind, how to this? The shared pointers in C++ and Boost implement this. Andrei Alexandrescu's Modern C++ Design contains a short overview of implementation choices (but does not cover weak pointers). GCC's implementation of std::share_ptr uses separate pointers to objects and their reference counts. The reference counts contain two counters, for strong and weak references. The actual object is dealloated when the strong count reaches zero. The reference counts are deallocated when the strong and weak counts reach zero. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-07 9:57 ` Florian Weimer @ 2011-05-08 8:08 ` Dmitry A. Kazakov 2011-05-08 8:46 ` Florian Weimer 0 siblings, 1 reply; 31+ messages in thread From: Dmitry A. Kazakov @ 2011-05-08 8:08 UTC (permalink / raw) On Sat, 07 May 2011 11:57:07 +0200, Florian Weimer wrote: > * Dmitry A. Kazakov: > >> On Sat, 07 May 2011 11:09:44 +0200, Florian Weimer wrote: >> >>> As usual, there is a trade-off between flexibility and overhead: if >>> the reference counters are kept separate, it is possible to >>> implement weak pointers and safe references to sub-components. >> >> Interesting. Do you have a certain schema in mind, how to this? > > The shared pointers in C++ and Boost implement this. Andrei > Alexandrescu's Modern C++ Design contains a short overview of > implementation choices (but does not cover weak pointers). > > GCC's implementation of std::share_ptr uses separate pointers to > objects and their reference counts. The reference counts contain two > counters, for strong and weak references. The actual object is > dealloated when the strong count reaches zero. The reference counts > are deallocated when the strong and weak counts reach zero. I mean a schema which could be usable for Ada. I see many problems, apart from tasking. If you want to refer components, you need to add the parent's count to the component's count in order to prevent its zeroing. When you create a reference you have to check if the counts already exist somewhere. Since nothing is allocated in the object, where get that information from? IMO weak references are quite useless if do not support notifications (when the last strong reference is removed). I.e. you need a list of weak reference holders. Maintaining such lists is a problem. It is more than count, it can be changed during notifications etc. One possible approach could be a storage pool maintaining allocating and maintaining counts for all object allocated there. It won't work for components, though. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-08 8:08 ` Dmitry A. Kazakov @ 2011-05-08 8:46 ` Florian Weimer 2011-05-08 9:32 ` Dmitry A. Kazakov 0 siblings, 1 reply; 31+ messages in thread From: Florian Weimer @ 2011-05-08 8:46 UTC (permalink / raw) * Dmitry A. Kazakov: > I mean a schema which could be usable for Ada. I see many problems, apart > from tasking. If you want to refer components, you need to add the parent's > count to the component's count in order to prevent its zeroing. The component wouldn't have its own count. A reference to a component would consist of a pointer to the component, combined with a pointer to the reference counters for the outmost object. > When you create a reference you have to check if the counts already > exist somewhere. Since nothing is allocated in the object, where > get that information from? You would only be able to create a reference from prefixes which contain references at certain points. For everything else, you'd have to use access values, as before. > IMO weak references are quite useless if do not support notifications (when > the last strong reference is removed). I.e. you need a list of weak > reference holders. I think they are supposed to be used for parent pointers in trees, for instance, to avoid the cycle issue. Not so much for finalization. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-08 8:46 ` Florian Weimer @ 2011-05-08 9:32 ` Dmitry A. Kazakov 2011-05-08 10:30 ` Florian Weimer 0 siblings, 1 reply; 31+ messages in thread From: Dmitry A. Kazakov @ 2011-05-08 9:32 UTC (permalink / raw) On Sun, 08 May 2011 10:46:05 +0200, Florian Weimer wrote: > * Dmitry A. Kazakov: > >> I mean a schema which could be usable for Ada. I see many problems, apart >> from tasking. If you want to refer components, you need to add the parent's >> count to the component's count in order to prevent its zeroing. > > The component wouldn't have its own count. A reference to a component > would consist of a pointer to the component, combined with a pointer > to the reference counters for the outmost object. Then a built-in access-to-component type might be a better solution. It would eliminate a need for components to be aliased. Since the offset is statically known (or a function that calculates it is), it need not to be kept anywhere. But the actual problem is that references to components look useless if cannot be mixed with plain access to the component types. Ada does not have means for that. E.g. classes of access types. >> When you create a reference you have to check if the counts already >> exist somewhere. Since nothing is allocated in the object, where >> get that information from? > > You would only be able to create a reference from prefixes which > contain references at certain points. OK, but you need to create the first reference somehow. That is what an embedded count does, the object is kind of a reference to itself. If that is dropped, one have to find another mechanism for that. >> IMO weak references are quite useless if do not support notifications (when >> the last strong reference is removed). I.e. you need a list of weak >> reference holders. > > I think they are supposed to be used for parent pointers in trees, for > instance, to avoid the cycle issue. Not so much for finalization. I rather use: parent-->child is a plain pointer, child-->parent is a strong reference. The most interesting cases for weak references are in the first line finalization notification. E.g. cached objects. They are referenced by weak reference by the cache monitor. When the object is finalized, the monitor synchronizes the object's actual state with the non-volatile memory etc. I think that the issue is too varying and complex to have it built-in. I would prefer if Ada provided mechanisms for implementation of such stuff at the library level. E.g. user-defined access types with primitive referencing, dereferencing, finalization operations. Classes of access types etc. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-08 9:32 ` Dmitry A. Kazakov @ 2011-05-08 10:30 ` Florian Weimer 2011-05-08 20:24 ` anon 2011-05-09 7:48 ` Dmitry A. Kazakov 0 siblings, 2 replies; 31+ messages in thread From: Florian Weimer @ 2011-05-08 10:30 UTC (permalink / raw) * Dmitry A. Kazakov: > Then a built-in access-to-component type might be a better solution. It > would eliminate a need for components to be aliased. Since the offset is > statically known (or a function that calculates it is), it need not to be > kept anywhere. You'd still have the safety hazard with the reference to the outer record. There are is some impact on encapsulation which has to be considered. And it's not going to help with the original problem (a safer replacement for discriminants with defaults). > OK, but you need to create the first reference somehow. Uhm, I had imagined you'd use an allocator for that. The whole thing is meant to be a bit similar to access values. >>> IMO weak references are quite useless if do not support notifications (when >>> the last strong reference is removed). I.e. you need a list of weak >>> reference holders. >> >> I think they are supposed to be used for parent pointers in trees, for >> instance, to avoid the cycle issue. Not so much for finalization. > > I rather use: parent-->child is a plain pointer, child-->parent is a > strong reference. Dereferencing a weak pointer incurs a run-time check and operations on the counters (if reference counting is used), and the parent pointer is only needed for some traversal operations, so weak pointers upwards seem the way to go. > The most interesting cases for weak references are in the first line > finalization notification. E.g. cached objects. You would get that with controlled types. I don't think weak references work for caches if you have reference counts and precise finalization because the last reference to the cached object goes away too soon. There are different types of references (sometimes called "weak", too) which are cleared by the memory manager if it cannot satisfy an allocation request, but this raises awkward concurrency issues, and this wouldn't actually need references, you'd just have to register those special references with the memory manager. > I think that the issue is too varying and complex to have it > built-in. I would prefer if Ada provided mechanisms for > implementation of such stuff at the library level. E.g. user-defined > access types with primitive referencing, dereferencing, finalization > operations. Classes of access types etc. A pure library implementation would make certain optimizations difficult or impossible: for example, link-time replacement of tasking-safe counter implementations when there is no tasking, or avoidance of repeated counter operations on the same object. It also requires a lot of mechanics, adding more complexity to the language than a built-in facility. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-08 10:30 ` Florian Weimer @ 2011-05-08 20:24 ` anon 2011-05-08 21:11 ` Simon Wright 2011-05-09 7:48 ` Dmitry A. Kazakov 1 sibling, 1 reply; 31+ messages in thread From: anon @ 2011-05-08 20:24 UTC (permalink / raw) Your two programs has pointed out a puzzle in the RM-2005. And that is does the definition of the standard Generic package Unchecked_Conversion violate the RM (6.5/(5.5/2). generic type Source(<>) is limited private; type Target(<>) is limited private; function Ada.Unchecked_Conversion(S : Source) return Target; pragma Convention(Intrinsic, Ada.Unchecked_Conversion); pragma Pure(Ada.Unchecked_Conversion); And when you compile the RM Ada 95 specification and body version of the Unchecked_Conversion using Ada 2005 compiler you get the following errors: (Ada 2005) cannot copy object of a limited type (RM-2005 6.5(5.5/2)) return by reference not permitted in Ada 2005 consider switching to return of access type But that suggest the RM standard generic Unchecked_Conversion is obsolete in its current version or is in error. And yes, I know that mostly the compiler emulates the Unchecked_Conversion body, but that not always an option. Now, since one reason the extended return statement was created was to handle "Limited Private" type the simple and return aggregate statements are out. And, using a simple design of the extended return statement causes semantic error in Ada 95/2005 because any reference to an unconstrained Target is not valid. Such as: return Result : Target do -- not valid, must be initialized -- in this form (Discriminant) Result := Target ( Object_Access.all ) ; end return ; So, how does one use this extended return statement to return a "Unconstrained Discriminated Limited Private" object for this type of generic function? Or is the Unchecked_Conversion generic function a violation of the RM 2005 rules? In <87pqntscwj.fsf@mid.deneb.enyo.de>, Florian Weimer <fw@deneb.enyo.de> writes: >* Dmitry A. Kazakov: > >> Then a built-in access-to-component type might be a better solution. It >> would eliminate a need for components to be aliased. Since the offset is >> statically known (or a function that calculates it is), it need not to be >> kept anywhere. > >You'd still have the safety hazard with the reference to the outer >record. There are is some impact on encapsulation which has to be >considered. And it's not going to help with the original problem (a >safer replacement for discriminants with defaults). > >> OK, but you need to create the first reference somehow. > >Uhm, I had imagined you'd use an allocator for that. The whole thing >is meant to be a bit similar to access values. > >>>> IMO weak references are quite useless if do not support notifications (when >>>> the last strong reference is removed). I.e. you need a list of weak >>>> reference holders. >>> >>> I think they are supposed to be used for parent pointers in trees, for >>> instance, to avoid the cycle issue. Not so much for finalization. >> >> I rather use: parent-->child is a plain pointer, child-->parent is a >> strong reference. > >Dereferencing a weak pointer incurs a run-time check and operations on >the counters (if reference counting is used), and the parent pointer >is only needed for some traversal operations, so weak pointers upwards >seem the way to go. > >> The most interesting cases for weak references are in the first line >> finalization notification. E.g. cached objects. > >You would get that with controlled types. > >I don't think weak references work for caches if you have reference >counts and precise finalization because the last reference to the >cached object goes away too soon. There are different types of >references (sometimes called "weak", too) which are cleared by the >memory manager if it cannot satisfy an allocation request, but this >raises awkward concurrency issues, and this wouldn't actually need >references, you'd just have to register those special references with >the memory manager. > >> I think that the issue is too varying and complex to have it >> built-in. I would prefer if Ada provided mechanisms for >> implementation of such stuff at the library level. E.g. user-defined >> access types with primitive referencing, dereferencing, finalization >> operations. Classes of access types etc. > >A pure library implementation would make certain optimizations >difficult or impossible: for example, link-time replacement of >tasking-safe counter implementations when there is no tasking, or >avoidance of repeated counter operations on the same object. It also >requires a lot of mechanics, adding more complexity to the language >than a built-in facility. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-08 20:24 ` anon @ 2011-05-08 21:11 ` Simon Wright 2011-05-10 6:27 ` anon 0 siblings, 1 reply; 31+ messages in thread From: Simon Wright @ 2011-05-08 21:11 UTC (permalink / raw) anon@att.net writes: > Your two programs has pointed out a puzzle in the RM-2005. And that is > does the definition of the standard Generic package Unchecked_Conversion > violate the RM (6.5/(5.5/2). "I beseech you, in the bowels of Christ, think it possible that you may be mistaken." O. Cromwell, 1650. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-08 21:11 ` Simon Wright @ 2011-05-10 6:27 ` anon 2011-05-10 14:39 ` Adam Beneschan 0 siblings, 1 reply; 31+ messages in thread From: anon @ 2011-05-10 6:27 UTC (permalink / raw) Better look again! Even though a compiler emulates the "Unchecked_Conversion" with a built-in "pragma inline" being enforced. The function still must be able to be written in Ada. That goes back to the origins of Ada and has not change. Plus, the function's source code can be inserted into a routine or re-named for use or testing of this routine. That is also apart of Ada. Any programmer should be able to simulated the Unchecked_Conversion function in Ada (non GNAT), such as: pragma Suppress ( All_Checks ) ; -- Ada 95/2005 Target_Object := Target ( Source_Object ) ; pragma Unsuppress ( All_Checks ) ; -- Ada 95/2005 Now, in Ada 83 there was no way to turn the checks back on after they were suppressed. The exception was to use a function where the scope of the suppressed checks would be limited to that function only, thus a reason for the creation of the generic "Unchecked_Conversion" function. The body of the "Unchecked_Conversion" function is: --------------------------------------- function Unchecked_Conversion ( S : Source ) return Target is -- Ada 83 a Suppress statement per check must be given -- pragma Suppress ( All_Checks ) ; begin -- This statement should compile because all checks have been -- turn off. Even without checks this routine must still comply -- with type conversion rules set in RM 4.6, which can limited -- or restriction the conversion. -- -- Starting with Ada 95 the semantic and expansion analysis -- must also, insure that the additional rules RM 13.9 -- ( 5 .. 10 ) are enforced. Since these rules can be derived -- from the legal rules for type conversion ( RM 4.6 ), these -- checks can be done in the while evaluating the type -- conversion expression. -- -- Ada 83, RM 13.10.2 ( 3 ) states the programmer is -- "responsibility to ensure that these conversions maintain -- the properties that are guaranteed by the language for -- objects of the target type." But the vendor can set -- restrictions. -- return Target ( S ) ; end Unchecked_Conversion ; --------------------------------------- Now in Ada 2005, RM 7.5 (1/2) states that a routine can not just copy a "limited private" object. RM 6.5 (5.1/2, 5.c/2 ) states that if the target is limited the function "must produce a ""new"" object" instead of just copying the object. Aka the "Unchecked_Conversion" which is a generic function is no longer just an inlined expression that is just a type conversions with all checks being disable. The function must now return a "new" object RM 6.5 (5.5/2, 5.c/2 ), by first requesting an new object from the Target's storage pool and then copying the Source data to that new object. So, in Ada 2005 the "Unchecked_Conversion" must be handled as a true generic function with a true return, instead of a built-in inline expression. But GNAT still just performs a simple copy. So, is GNAT or the RM or is the generic "Unchecked_Conversion" function in error? In <m2y62g7v92.fsf@pushface.org>, Simon Wright <simon@pushface.org> writes: >anon@att.net writes: > >> Your two programs has pointed out a puzzle in the RM-2005. And that is >> does the definition of the standard Generic package Unchecked_Conversion >> violate the RM (6.5/(5.5/2). > >"I beseech you, in the bowels of Christ, think it possible that you may > be mistaken." > O. Cromwell, 1650. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-10 6:27 ` anon @ 2011-05-10 14:39 ` Adam Beneschan 2011-05-11 20:39 ` anon 0 siblings, 1 reply; 31+ messages in thread From: Adam Beneschan @ 2011-05-10 14:39 UTC (permalink / raw) On May 9, 11:27 pm, a...@att.net wrote: > Better look again! > > Even though a compiler emulates the "Unchecked_Conversion" with a built-in > "pragma inline" being enforced. The function still must be able to be > written in Ada. No, it doesn't. Show me a rule in the RM that says it does. You can't. There isn't one. -- Adam ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-10 14:39 ` Adam Beneschan @ 2011-05-11 20:39 ` anon 2011-05-12 0:51 ` Randy Brukardt 2011-05-12 5:51 ` AdaMagica 0 siblings, 2 replies; 31+ messages in thread From: anon @ 2011-05-11 20:39 UTC (permalink / raw) The RM 13.9 (3) defines the Unchecked_Conversion function with pragma Convention ( Intrinsic, Ada.Unchecked_Conversion ) ; Intrinsic is built-in" and RM C.1 ( 10 ) implies inlining to reduce overhead of this function and RM 13.8 ( 15 ) helps reduces the code to that of an inline Machine Code Insertions. Proof basically come from RM 6.3.1 ( 4 ), RM 13.9 ( 15 ) and C.1 ( 10 ). RM 6.3.1 Conformance Rules 4 "The Intrinsic calling convention represents subprograms that are ``built in'' to the compiler." ... RM 13.9 Unchecked Type Conversions 15 The implementation should not generate unnecessary run-time checks to ensure that the representation of S is a representation of the target type. It should take advantage of the permission to return by reference when possible. Restrictions on unchecked conversions should be avoided unless required by the target environment. RM C.1 Access to Machine Operations 10 "The implementation should ensure that little or no overhead is associated with calling intrinsic and machine-code subprograms" Associative RMs RM 13.8 Machine Code Insertions 11 "(17) Intrinsic subprograms (see 6.3.1, ``Conformance Rules'') can also be used to achieve machine code insertions." ... RM C.1 Access to Machine Operations 6 "The implementation shall document the overhead associated with calling machine-code or intrinsic subprograms, as compared to a fully-inlined call, and to a regular out-of-line call." Now in Ada 2005, RM 7.5 (1/2) states that a routine can not just copy a "limited private" object. RM 6.5 (5.1/2, 5.c/2 ) states that if the target is limited the function "must produce a ""new"" object" instead of just copying the object. Aka the "Unchecked_Conversion" which is a generic function is no longer just an inlined expression that is just a type conversions with all checks being disable. The function must now return a "new" object RM 6.5 (5.5/2, 5.c/2 ), by first requesting an new object from the Target's storage pool and then copying the Source data to that new object. So, in Ada 2005 the "Unchecked_Conversion" must be handled as a true generic function with a true return, instead of a built-in inline expression. But GNAT still just performs a simple copy. So, is GNAT or the RM or is the generic "Unchecked_Conversion" function in error? In <715a5498-095c-4e61-8a09-8510c19b2553@s16g2000prf.googlegroups.com>, Adam Beneschan <adam@irvine.com> writes: >On May 9, 11:27=A0pm, a...@att.net wrote: >> Better look again! >> >> Even though a compiler emulates the "Unchecked_Conversion" with a built-i= >n >> "pragma inline" being enforced. =A0The function still must be able to be >> written in Ada. > >No, it doesn't. Show me a rule in the RM that says it does. You >can't. There isn't one. > > -- Adam ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-11 20:39 ` anon @ 2011-05-12 0:51 ` Randy Brukardt 2011-05-13 0:47 ` anon 2011-05-12 5:51 ` AdaMagica 1 sibling, 1 reply; 31+ messages in thread From: Randy Brukardt @ 2011-05-12 0:51 UTC (permalink / raw) <anon@att.net> wrote in message news:iqes6i$18g$1@speranza.aioe.org... > The RM 13.9 (3) defines the Unchecked_Conversion function with > > pragma Convention ( Intrinsic, Ada.Unchecked_Conversion ) ; > > Intrinsic is built-in" and RM C.1 ( 10 ) implies inlining to reduce > overhead of this function and RM 13.8 ( 15 ) helps reduces the code > to that of an inline Machine Code Insertions. > > > Proof basically come from RM 6.3.1 ( 4 ), RM 13.9 ( 15 ) and > C.1 ( 10 ). > > RM 6.3.1 Conformance Rules > > 4 "The Intrinsic calling convention represents subprograms that > are ``built in'' to the compiler." ... > > RM 13.9 Unchecked Type Conversions > > 15 The implementation should not generate unnecessary run-time > checks to ensure that the representation of S is a > representation of the target type. It should take advantage of > the permission to return by reference when possible. > Restrictions on unchecked conversions should be avoided unless > required by the target environment. > > RM C.1 Access to Machine Operations > > 10 "The implementation should ensure that little or no overhead > is associated with calling intrinsic and machine-code > subprograms" > > > > Associative RMs > > RM 13.8 Machine Code Insertions > > 11 "(17) Intrinsic subprograms (see 6.3.1, ``Conformance Rules'') > can also be used to achieve machine code insertions." ... > > RM C.1 Access to Machine Operations > > 6 "The implementation shall document the overhead associated > with calling machine-code or intrinsic subprograms, as > compared to a fully-inlined call, and to a regular > out-of-line call." > > > > Now in Ada 2005, RM 7.5 (1/2) states that a routine can not just copy > a "limited private" object. RM 6.5 (5.1/2, 5.c/2 ) states that if > the target is limited the function "must produce a ""new"" object" > instead of just copying the object. > > Aka the "Unchecked_Conversion" which is a generic function is no > longer just an inlined expression that is just a type conversions > with all checks being disable. The function must now return a "new" > object RM 6.5 (5.5/2, 5.c/2 ), by first requesting an new object > from the Target's storage pool and then copying the Source data to > that new object. So, in Ada 2005 the "Unchecked_Conversion" must be > handled as a true generic function with a true return, instead of a > built-in inline expression. > > But GNAT still just performs a simple copy. So, is GNAT or the RM > or is the generic "Unchecked_Conversion" function in error? You, of course. :-) Your language-lawyering skills need some work. 13.9(12) (an implementation permission) says that an implementation can return the result of an unchecked_conversion "by reference". Especially note the second sentence of that rule, which explains the intent. Randy. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-12 0:51 ` Randy Brukardt @ 2011-05-13 0:47 ` anon 2011-05-13 0:58 ` Adam Beneschan 2011-05-13 5:31 ` AdaMagica 0 siblings, 2 replies; 31+ messages in thread From: anon @ 2011-05-13 0:47 UTC (permalink / raw) -- -- May be this program will help explain the problem. -- -- Plus, -- RM 13.9 (12) -- say the function can copy "by reference, if the -- Source type is not a by-copy type." In Ada 2005, the definition -- of Source type for the Unchecked_Conversion is still a "limited -- private" which is a "Limited Type, and all Limited types (RM 7.5) -- are now a by-copy type. So, Unchecked_Conversion must now use -- only "by-copy" type of copying. This suggest that the RM 13.9 (12) -- paragraph may be in Error an needs to be update at least for Ada -- 2012. And corrected in 2005. -- -- RM 7.5 ( 1.b/2 ), Suggest that the copying "Limited Types" by -- reference was for Ada 95. That changes for Ada 2005. -- -- This may also explain what Robert Dewar was taking about in -- AI95-00318-02. -- -- -- Testing the Generic Unchecked_Conversion function. -- By copying and renaming the function to Ada05_Conversion. -- with Interfaces ; with Text_IO ; -- procedure Test_UC is use Interfaces ; -- package Byte_IO is new Text_IO.Modular_IO ( Unsigned_8 ) ; package U32_IO is new Text_IO.Modular_IO ( Unsigned_32 ) ; -- type Boolean_Array is array ( Natural range <> ) of Boolean ; pragma Pack ( Boolean_Array ) ; -- -- Limited type: must copy by "by-copy type" -- type Record_Type is limited record Flags : Boolean_Array ( 0 .. 31 ) ; end record ; pragma Pack ( Record_Type ) ; -- ------------------ -- -- Ada05_Conversion -- -- ------------------ -- generic type Source (<>) is limited private ; type Target (<>) is limited private ; function Ada05_Conversion ( S : Source ) return Target ; function Ada05_Conversion ( S : Source ) return Target is pragma Suppress ( All_Checks ) ; begin -- Compiling Error messages: -- (Ada 2005) cannot copy object of a limited type -- (RM-2005 6.5(5.5/2)) -- consider switching to return of access type -- -- Additional References for reasoning: -- RM 7.5 Limited Types ( 1/2, 1.a, 1.b/2 ) -- return Target ( S ) ; end Ada05_Conversion ; -- function To_Byte is new Ada05_Conversion ( Source => Unsigned_8, Target => Character ) ; function To_Unsigned_32 is new Ada05_Conversion ( Source => Record_Type, Target => Unsigned_32 ) ; C : Character := 'c' ; S : Unsigned_8 := 16#41# ; -- 'A' R0 : Record_Type ; R1 : Unsigned_32 ; begin -- -- Copy a simple unsigned_8 to character -- C := To_Byte ( S ) ; -- Perform conversion -- -- Should display: -- Test 1: C := 'A' -- Text_IO.Put ( "Test 1: " ) ; Text_IO.Put ( "C := '" ) ; Text_IO.Put ( C ) ; Text_IO.Put ( ''' ) ; Text_IO.New_Line ; -- R0.Flags ( 28 .. 31 ) := ( others => True ) ; R0.Flags ( 00 .. 27 ) := ( others => False ) ; -- -- Copy a limited record to a Unsigned_32 -- R1 := To_Unsigned_32 ( R0 ) ; -- Perform conversion -- -- Should display: -- Test 2: R0 := 2#11110000000000000000000000000000# -- Text_IO.Put ( "Test 2: " ) ; Text_IO.Put ( "R0 := " ) ; U32_IO.Put ( R1, base => 2 ) ; Text_IO.New_Line ; end Test_UC ; In <iqfaud$ggn$1@munin.nbi.dk>, "Randy Brukardt" <randy@rrsoftware.com> writes: ><anon@att.net> wrote in message news:iqes6i$18g$1@speranza.aioe.org... >> The RM 13.9 (3) defines the Unchecked_Conversion function with >> >> pragma Convention ( Intrinsic, Ada.Unchecked_Conversion ) ; >> >> Intrinsic is built-in" and RM C.1 ( 10 ) implies inlining to reduce >> overhead of this function and RM 13.8 ( 15 ) helps reduces the code >> to that of an inline Machine Code Insertions. >> >> >> Proof basically come from RM 6.3.1 ( 4 ), RM 13.9 ( 15 ) and >> C.1 ( 10 ). >> >> RM 6.3.1 Conformance Rules >> >> 4 "The Intrinsic calling convention represents subprograms that >> are ``built in'' to the compiler." ... >> >> RM 13.9 Unchecked Type Conversions >> >> 15 The implementation should not generate unnecessary run-time >> checks to ensure that the representation of S is a >> representation of the target type. It should take advantage of >> the permission to return by reference when possible. >> Restrictions on unchecked conversions should be avoided unless >> required by the target environment. >> >> RM C.1 Access to Machine Operations >> >> 10 "The implementation should ensure that little or no overhead >> is associated with calling intrinsic and machine-code >> subprograms" >> >> >> >> Associative RMs >> >> RM 13.8 Machine Code Insertions >> >> 11 "(17) Intrinsic subprograms (see 6.3.1, ``Conformance Rules'') >> can also be used to achieve machine code insertions." ... >> >> RM C.1 Access to Machine Operations >> >> 6 "The implementation shall document the overhead associated >> with calling machine-code or intrinsic subprograms, as >> compared to a fully-inlined call, and to a regular >> out-of-line call." >> >> >> >> Now in Ada 2005, RM 7.5 (1/2) states that a routine can not just copy >> a "limited private" object. RM 6.5 (5.1/2, 5.c/2 ) states that if >> the target is limited the function "must produce a ""new"" object" >> instead of just copying the object. >> >> Aka the "Unchecked_Conversion" which is a generic function is no >> longer just an inlined expression that is just a type conversions >> with all checks being disable. The function must now return a "new" >> object RM 6.5 (5.5/2, 5.c/2 ), by first requesting an new object >> from the Target's storage pool and then copying the Source data to >> that new object. So, in Ada 2005 the "Unchecked_Conversion" must be >> handled as a true generic function with a true return, instead of a >> built-in inline expression. >> >> But GNAT still just performs a simple copy. So, is GNAT or the RM >> or is the generic "Unchecked_Conversion" function in error? > >You, of course. :-) Your language-lawyering skills need some work. > >13.9(12) (an implementation permission) says that an implementation can >return the result of an unchecked_conversion "by reference". Especially note >the second sentence of that rule, which explains the intent. > > Randy. > > > > > > ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-13 0:47 ` anon @ 2011-05-13 0:58 ` Adam Beneschan 2011-05-13 5:31 ` AdaMagica 1 sibling, 0 replies; 31+ messages in thread From: Adam Beneschan @ 2011-05-13 0:58 UTC (permalink / raw) On May 12, 5:47 pm, a...@att.net wrote: > -- > -- May be this program will help explain the problem. > -- > -- Plus, > -- RM 13.9 (12) -- say the function can copy "by reference, if the > -- Source type is not a by-copy type." In Ada 2005, the definition > -- of Source type for the Unchecked_Conversion is still a "limited > -- private" which is a "Limited Type, and all Limited types (RM 7.5) > -- are now a by-copy type. Bzzzt!! Please read 6.2. If you're going to try be be a language lawyer, you might want to consult the RM to find out what the terms you use actually mean, rather than guessing. -- Adam ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-13 0:47 ` anon 2011-05-13 0:58 ` Adam Beneschan @ 2011-05-13 5:31 ` AdaMagica 1 sibling, 0 replies; 31+ messages in thread From: AdaMagica @ 2011-05-13 5:31 UTC (permalink / raw) On 13 Mai, 02:47, a...@att.net wrote: > -- > -- May be this program will help explain the problem. > -- (erased a lot of irrelevant stuff) > -- ------------------ -- > -- Ada05_Conversion -- > -- ------------------ -- > generic > type Source (<>) is limited private ; > type Target (<>) is limited private ; > function Ada05_Conversion ( S : Source ) return Target ; > > function Ada05_Conversion ( S : Source ) return Target is > > pragma Suppress ( All_Checks ) ; > > begin > -- Compiling Error messages: > -- (Ada 2005) cannot copy object of a limited type > -- (RM-2005 6.5(5.5/2)) > -- consider switching to return of access type > -- > -- Additional References for reasoning: > -- RM 7.5 Limited Types ( 1/2, 1.a, 1.b/2 ) > -- > return Target ( S ) ; What you are writing here is a normal type conversion with certain rules (RM 4.6), not an unchecked type conversion (RM 13.9). For a type conversion to be legal, the types involved have to be convertible. Source and Target are not convertible, so this whole thing is illegal for several reasons. > end Ada05_Conversion ; RTFM ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-11 20:39 ` anon 2011-05-12 0:51 ` Randy Brukardt @ 2011-05-12 5:51 ` AdaMagica 2011-05-12 12:09 ` Robert A Duff 2011-05-12 14:40 ` Adam Beneschan 1 sibling, 2 replies; 31+ messages in thread From: AdaMagica @ 2011-05-12 5:51 UTC (permalink / raw) Randy has already given the answer. Let me add a further remark (but note, I'm no language lawyer): You argue about the return statement. But there is no return statement involved in the RM with Unchecked_Conversion, it's just compiler magic that is being performed. (Being intrinsic, Unchecked_Conversion need not be implemented in Ada. In fact, I think there is no code at all, it just takes the bit pattern as is and reinterpretes it.) Run the following test program: package UC is type LP1 is limited private; procedure Set (X: out LP1; to: Integer); type LP2 is limited private; private type LP1 is limited record I: Integer := 1234; end record; type LP2 is limited record I: Integer := Integer'First; end record; end UC; package body UC is procedure Set (X: out LP1; to: Integer) is begin X.I := to; end Set; end UC; with Ada.Text_IO; with Ada.Unchecked_Conversion; with UC; procedure Test_UC is function LP1_LP2 is new Ada.Unchecked_Conversion (UC.LP1, UC.LP2); function LP2_I is new Ada.Unchecked_Conversion (UC.LP2, Integer); X: UC.LP1; Y: constant UC.LP2 := LP1_LP2 (X); -- new object built in place Z: UC.LP2 renames LP1_LP2 (X); -- read-only view begin -- expected result Ada.Text_IO.Put_Line (Integer'Image (LP2_I (Y)) & Integer'Image (LP2_I (Z))); -- 1234 1234 UC.Set (X, -100); Ada.Text_IO.Put_Line (Integer'Image (LP2_I (Y)) & Integer'Image (LP2_I (Z))); -- 1234-100 end Test_UC; ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-12 5:51 ` AdaMagica @ 2011-05-12 12:09 ` Robert A Duff 2011-05-12 14:40 ` Adam Beneschan 1 sibling, 0 replies; 31+ messages in thread From: Robert A Duff @ 2011-05-12 12:09 UTC (permalink / raw) AdaMagica <christ-usch.grein@t-online.de> writes: >... (Being intrinsic, Unchecked_Conversion need > not be implemented in Ada. It's true that Unchecked_Conversion need not be implemented in Ada -- it can be implemented however the compiler writer likes. But it's got nothing to do with "intrinsic". Every feature can be implemented however the compiler writer likes. For example, Text_IO.Put_Line, which is not intrinsic, need not be implemented in Ada. Intrinsic just means you can't do 'Access and similar things. - Bob ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-12 5:51 ` AdaMagica 2011-05-12 12:09 ` Robert A Duff @ 2011-05-12 14:40 ` Adam Beneschan 2011-05-14 0:30 ` Randy Brukardt 1 sibling, 1 reply; 31+ messages in thread From: Adam Beneschan @ 2011-05-12 14:40 UTC (permalink / raw) On May 11, 10:51 pm, AdaMagica <christ-usch.gr...@t-online.de> wrote: > Randy has already given the answer. > > Let me add a further remark (but note, I'm no language lawyer): > > You argue about the return statement. But there is no return statement > involved in the RM with Unchecked_Conversion, it's just compiler magic > that is being performed. (Being intrinsic, Unchecked_Conversion need > not be implemented in Ada. In fact, I think there is no code at all, > it just takes the bit pattern as is and reinterpretes it.) I think the poster's argument (inasmuch as I could follow it) was that although Unchecked_Conversion is implemented via compiler magic, it has to be defined in such a way that it *could* be written in Ada, and this would be a problem because you couldn't write a RETURN statement that would make it work. Or something like that. The basic problem is that there is no such rule (and never has been) that the function has to be writeable in Ada, despite the poster's assertion that "that goes back to the origins of Ada and has not change", so the whole argument is based on a false premise. The poster tried to make an argument to prove this premise, but I'm still trying to figure out what this argument was. It seems to be based on reading much more into certain language rules than is written there. -- Adam ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-12 14:40 ` Adam Beneschan @ 2011-05-14 0:30 ` Randy Brukardt 0 siblings, 0 replies; 31+ messages in thread From: Randy Brukardt @ 2011-05-14 0:30 UTC (permalink / raw) "Adam Beneschan" <adam@irvine.com> wrote in message news:20de6e7b-8b76-4d84-877b-714e794aa7c1@17g2000prr.googlegroups.com... ... > It seems to be based on reading much more > into certain language rules than is written there. And ignoring the ones that are there. In a standard as large as Ada's, you can probably prove that unicorns exist that way... Randy. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-08 10:30 ` Florian Weimer 2011-05-08 20:24 ` anon @ 2011-05-09 7:48 ` Dmitry A. Kazakov 1 sibling, 0 replies; 31+ messages in thread From: Dmitry A. Kazakov @ 2011-05-09 7:48 UTC (permalink / raw) On Sun, 08 May 2011 12:30:04 +0200, Florian Weimer wrote: > * Dmitry A. Kazakov: > >> Then a built-in access-to-component type might be a better solution. It >> would eliminate a need for components to be aliased. Since the offset is >> statically known (or a function that calculates it is), it need not to be >> kept anywhere. > > You'd still have the safety hazard with the reference to the outer > record. There are is some impact on encapsulation which has to be > considered. And it's not going to help with the original problem (a > safer replacement for discriminants with defaults). I don't think there is a solution for that. Variant components have no static scope, so any reference can only be a weak one with dynamic checks. I don't think that dynamic checks is a good idea. On the contrary, dynamic accessibility checks is the most damaging feature Ada ever had. >> OK, but you need to create the first reference somehow. > > Uhm, I had imagined you'd use an allocator for that. The whole thing > is meant to be a bit similar to access values. It would not work with stack-allocated objects. Anyway, if you do it upon allocation, then in effect the reference count is always there, "put" into the object either explicitly (via MI) or implicitly (via the storage pool). >>>> IMO weak references are quite useless if do not support notifications (when >>>> the last strong reference is removed). I.e. you need a list of weak >>>> reference holders. >>> >>> I think they are supposed to be used for parent pointers in trees, for >>> instance, to avoid the cycle issue. Not so much for finalization. >> >> I rather use: parent-->child is a plain pointer, child-->parent is a >> strong reference. > > Dereferencing a weak pointer incurs a run-time check and operations on > the counters (if reference counting is used), and the parent pointer > is only needed for some traversal operations, so weak pointers upwards > seem the way to go. I considered that schema, but then dropped it. It was much simpler to have children to hold their parents. Parent need not to have a reference to its children because in the scenarios I considered, parent always controls its children explicitly, e.g. when the tree is manipulated. >> The most interesting cases for weak references are in the first line >> finalization notification. E.g. cached objects. > > You would get that with controlled types. References must be "controlled" anyway, in the sense that reference finalization is not null. > I don't think weak references work for caches if you have reference > counts and precise finalization because the last reference to the > cached object goes away too soon. Before the target object finalization occurs, all weak references get notified and then invalidated when the strong count reaches zero. I don't think that weak references asynchronously going invalid is a good idea. Especially in tasking environment, where you cannot safely dereference weak references, you have to temporarily promote them to strong references. >> I think that the issue is too varying and complex to have it >> built-in. I would prefer if Ada provided mechanisms for >> implementation of such stuff at the library level. E.g. user-defined >> access types with primitive referencing, dereferencing, finalization >> operations. Classes of access types etc. > > A pure library implementation would make certain optimizations > difficult or impossible: for example, link-time replacement of > tasking-safe counter implementations when there is no tasking, or > avoidance of repeated counter operations on the same object. Yes, but I would buy that. Hardware is cheaper than the software. When will first implementations of such optimizations appear? How soon will they become bug-free? How many generations of hardware will change in that period of time? > It also > requires a lot of mechanics, adding more complexity to the language > than a built-in facility. No, it should simplify the language because this mechanics looks quite universal to me. In particular it could make "access", "controlled" and "tagged" types library level implementations. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-05-07 9:09 ` Florian Weimer 2011-05-07 9:28 ` Dmitry A. Kazakov @ 2011-05-09 20:41 ` Randy Brukardt 1 sibling, 0 replies; 31+ messages in thread From: Randy Brukardt @ 2011-05-09 20:41 UTC (permalink / raw) "Florian Weimer" <fw@deneb.enyo.de> wrote in message news:877ha2op0n.fsf@mid.deneb.enyo.de... >* Randy Brukardt: > >> Actually, I think T.T'Access is illegal, because the component T is not >> declared as aliased. You can take 'Access of the parameter T, but not of >> some random component (as is currently the case in Ada). > > Wouldn't it work if the component T was aliased? Maybe, but there are fairly strong rules on which components can have 'Access taken when they are discriminant dependent. (Note that these rules are not about aliasing, but rather to detect cases where components disappear because of direct modification. But of course they prevent a lot of aliasing problems as well.) ... >> But this looks like an attempt to shot yourself in the foot; it's hardly >> likely to happen by accident. Remember that no programming language can >> really defend against malicious programmers; the point is to defend >> against >> sloppy or careless programmers [which is all of us, sooner or later]. > > My first answer that was rather confrontational. Let me just say that > this is the line of argument used by C advocates. How so? C makes no attempt to detect problems caused by sloppy or careless programmers. It says that you shouldn't be sloppy or careless. Ada says that we'll detect errors caused by sloppiness or carelessness, but not by maliciousness (as that was thought to make the language unusable). Just to give an example unrelated to the topic of discussion, you can use Unchecked_Conversion to break privacy - just dupication the full type declaration from the private part into your own code and Unchecked_Convert to/from the original private type to your full declaration. This clearly does all kinds of violence to the typing model, but it is not going to happen by accident. So Ada allows Unchecked_Conversion even though it can do untold damage if abused. >> For instance, the tampering checks of the Ada containers implicit in >> the new Reference routines can be defeated by the use of >> Unchecked_Deallocation on the return object. But this is not >> something that is reasonably going to happen by accident, and it is >> out in the open for program reviewers (both tools and humans) to >> find. It's not worth worrying about. > > It's true that use-after-free errors typically result in exactly the > same type safety violation. But they seem to be rather hard to spot > in COTS software, and I'm not sure that the programming languages are > to blame. Manual memory management is just hard. I think you are missing the point. If you do something abusive in the code, you can circumvent the checks. But that doesn't happen by accident. Also note that the problem I was referring to has nothing to do with use-after-free; the problem is that the Unchecked_Deallocation triggers an early finalization while you still can be holding a valid copy of the wrapped access value. >>> It seems to me that you need a C++-like rule that accessing an object >>> through a view which is not compatible with its dynamic type is >>> erroneous. And this would be quite a defeat. >> >> But that's exactly what 3.7.2(4) is. And it has been in Ada since the >> beginning. > > 3.7.2(4) has very precise syntact requirements ("a name denoting a > subcomponent that depends on discriminants" etc.). I don't think it > covers the new possibilities introduced by aliased parameters. Sorry, but there aren't any new possibilities, unless we all have missed something very fundemental. Ada has always allowed composite parameters to be passed by reference, and most compilers in fact do that. You can take 'Unchecked_Access of components of any parameters that you want. You probably can't take 'Access of those components (the accessibility is local). Aliased parameters have very little effect on this situation. The accessibility is changed very slightly so that it can be returned as part of a return object, but only in cases that already have very short lifetimes (mostly access discriminants and anonymous access returns). The parameter is guaranteed to have the same lifetime as the return object. So in virtually all cases, you'll still need to use 'Unchecked_Access to get access to components. The problem here is parameter aliasing, and that was a problem with programming languages long before Ada was designed. Ada didn't try to solve it, and it's now 30 years too late to do so. >> Also note that a lot of renaming and 'Access of discriminant-dependent >> components is not allowed; we've tighened those rules with each version >> of >> Ada. > > It's not clear to me if these rules are valuable when its easy to > circumvent them. They mostly seem to increase the complexity of the > language. Ada has never made any attempt to prevent malicious programmers; to do so would require having no low-level features in the language. The problem here (if there is a problem) is that this might happen by accident. But there is no more likelyhood of that now than in Ada 83 (and I'd argue it is a lot safer now, rather than using untyped 'Address to access components). >>> The difficulty I alluded lies in the answer to this question: Are >>> aliased parameters at fault, or are discriminants with defaults to >>> blame? >> >> Almost certainly discriminants with defaults, since the problem has >> existed in Ada since Ada 83, and we are not making it any worse >> (that would be impossible, IMHO). > > I tend to agree. Would it make sense to deprecate discriminants with > defaults? I don't think so, it is much too fundemental of a feature. Note that the problem here is really parameter aliasing -- any time that a parameter can be accessed both by the parameter name and put some other name there is possibly trouble. (At least, you have a dependence on the parameter passing implementation, which Ada doesn't specify for many kinds of types.) Note that there are potentially problems for *any* kind of type -- this is precisely why Ada requires by-copy parameter passing for elementary types. It's not limited to discriminants with defaults. Ada 2012 has added some attributes which can be used to test if this is happening (of course, that requires knowing that there is a potential problem). Beyond that, it is relatively easy for tools that have the entire source code available to check for such things -- but it isn't a problem that the language (which has to allow compilation of partial programs) can fix. > ... As a replacement, smart pointers could be added to the > language, with syntactical overhead compared to access types, which > would allow programmers to replace discriminants with default with a > smart pointer to an unconstrained type. Implementations would be > allowed to use reference counting, or full garbage collection. > However, they would have to ensure that references to the object held > by the smart pointer would remain live for the time any reference to > it or its components exists (an actual subprogram parameter, for > instance). There would be no promise that cycles do not result in > memory leaks. Tucker tried to propose something like this (see AI05-0111-1). The feedback that we got (especially from the Ada conferences) was that it is just too complex to use. The complaint is that if you are going to work that hard, you probably would just as soon use real garbage collection. Instead, we decided to address the two things that users can't write themselves -- control over finalization of allocated objects, and convinient syntax for user-defined pointers. Thus Ada 2012 adds subpools and the Reference aspect, and nothing else. We hope that if smart pointers are really a good idea, that users and implementers will create implementations using these building blocks. And then we can consider standardizing something in Ada 2020 if they are being used a lot. It doesn't make much sense to standardize something that few people appear to be interested in (even if it might be a good idea in a vacuum). Randy. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: A hole in Ada type safety 2011-04-30 16:16 ` Florian Weimer 2011-04-30 23:39 ` Randy Brukardt @ 2011-05-14 23:47 ` anon 1 sibling, 0 replies; 31+ messages in thread From: anon @ 2011-05-14 23:47 UTC (permalink / raw) AdaMagica: Some times a programmer may need a only a few checks suppressed instead of all checks as with the RM Unchecked_Conversion function. Then there's the idea of creating your own function just like some here will say create your own version of an Integer instead of using the one defined in the Standard package. Now, I have one simple designed Unchecked_Conversion function that work for any Ada 83 compiler, and a second version that works for any Ada 95 compiler for all types used in that version of the language. Include a "unconstrained discriminant limited private" type. Florian Weimer: My design converts all types and I did not need to use aliased or tag type to add the discriminant-dependent feature. Just play with it a little and you will see the answer. Note: One thing. Any and all Unchecked_Conversion function just like Machine_Code Insertions adds a safety risk to the party. Plus, the conversion functions are easier to hide in a large scale project. This all started because of an answer that "Robert Duff" gave about about AARM-3.7.2(4,4.a), which refers to AI83_00585 and "Erroneous Execution". In looking at that Ada Issue I see only a compiler design error not an "Erroneous Execution" error. The fix is for the compiler to add either two or three checks in the elaborate code depending how the compiler evaluate the equation, which means no "Erroneous Execution". But in 15 plus years GNAT has not correct this compiler identifiable error which makes me think how many more "Erroneous Execution" has GNAT and other Ada venders skip fixing making the language less secure. Also, I knew that Adam and Randy were just talk!!! Makes me think that they are waiting for someone to create an example of the "extended return statement" so, they can learn how to use it. Because in looking at the AIs for 95 and 2005 and the ACVC 3.0 there is no example of an "extended return statement" for any "unconstrained discriminant limited private" except for those that call a function. But that not always possible. In <87tydfbtp3.fsf@mid.deneb.enyo.de>, Florian Weimer <fw@deneb.enyo.de> writes: >* Robert A. Duff: > >> Florian Weimer <fw@deneb.enyo.de> writes: >> >>> I don't know if this is a new observation---I couldn't find >>> documentation for it. >> >> It's not new. It is documented in AARM-3.7.2(4,4.a), >> which dates back to Ada 83 days. > >Ah. I didn't realize that call to Convert was already erroneous. > >It does not seem possible to extend the restrictions on 'Access >prefixes to to subprogram parameters (due to the way controlled types >are implemented, for example). > >>> Our implementation lacks the full power of Ada.Unchecked_Conversion >>> because it does not supported limited or unconstrained types. However, >>> it is sufficient to break type safety. >> >> Yes. Anything that is erroneous necessarily breaks type safety. >> If you look up "erroneous execution" in the index, you'll find >> them all. > >My concern was that this was not explicitly labeled as erroneous. 8-) > >> Your comment, "This note shows that a combination of safe-looking >> language features can be used to undermine type safety, too." >> is the key point. It is indeed unfortunate when "safe-looking" >> features can be erroneous. > >And once there is something like this in the language, it is difficult >to decide if a new addition (such as aliased parameters) make things >worse or not. ^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2011-05-14 23:47 UTC | newest] Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2011-04-30 8:41 A hole in Ada type safety Florian Weimer 2011-04-30 11:56 ` Robert A Duff 2011-04-30 15:27 ` Gavino 2011-04-30 16:16 ` Florian Weimer 2011-04-30 23:39 ` Randy Brukardt 2011-05-01 10:26 ` Florian Weimer 2011-05-03 1:40 ` Randy Brukardt 2011-05-03 16:57 ` Robert A Duff 2011-05-07 9:09 ` Florian Weimer 2011-05-07 9:28 ` Dmitry A. Kazakov 2011-05-07 9:57 ` Florian Weimer 2011-05-08 8:08 ` Dmitry A. Kazakov 2011-05-08 8:46 ` Florian Weimer 2011-05-08 9:32 ` Dmitry A. Kazakov 2011-05-08 10:30 ` Florian Weimer 2011-05-08 20:24 ` anon 2011-05-08 21:11 ` Simon Wright 2011-05-10 6:27 ` anon 2011-05-10 14:39 ` Adam Beneschan 2011-05-11 20:39 ` anon 2011-05-12 0:51 ` Randy Brukardt 2011-05-13 0:47 ` anon 2011-05-13 0:58 ` Adam Beneschan 2011-05-13 5:31 ` AdaMagica 2011-05-12 5:51 ` AdaMagica 2011-05-12 12:09 ` Robert A Duff 2011-05-12 14:40 ` Adam Beneschan 2011-05-14 0:30 ` Randy Brukardt 2011-05-09 7:48 ` Dmitry A. Kazakov 2011-05-09 20:41 ` Randy Brukardt 2011-05-14 23:47 ` anon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox