From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.3 required=5.0 tests=BAYES_00,INVALID_MSGID,XPRIO autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,fbb30b3a572ba6d0,start X-Google-Attributes: gid103376,public From: "Frank J. Lhota" Subject: Finalization and Self Assignment Date: 2000/02/29 Message-ID: #1/1 X-Deja-AN: 591377627 X-Priority: 3 X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2919.6600 X-Trace: client 951844890 38.163.203.81 (Tue, 29 Feb 2000 12:21:30 EST) X-MSMail-Priority: Normal NNTP-Posting-Date: Tue, 29 Feb 2000 12:21:30 EST Newsgroups: comp.lang.ada Date: 2000-02-29T00:00:00+00:00 List-Id: I find the Ada controlled object facility to be a convenient mechanism for controlling the initialization, assignment, and finalization of an object. There is a potential problem with this facility, however, that needs to be addressed: what happens when a variable is assigned to itself? Obviously, no one writes statements like "x := x;" in real programs, but self assignment can occur in boundary cases for reasonable assignments such as List (I) := List (J); when I equals J, or This_Ptr.all := That_Ptr.all; when This_Ptr and That_Ptr are the same access values. Self assignment is generally a non-op, and may never do anything terribly useful. Still it should not be a dangerous operation, and unfortunately it can undermine finalization and adjustments. For example, assume that we are implementing an object whose memory requirements varies widely. We could represent the object using access values, and use the Finalization package to do the pointer maintenance: type My_Object_Implementation ( Length : Natural ) is record ... end record; type My_Object_Implementation_Ptr is access all My_Object_Implementation; type My_Object is new Ada.Finalization.Controlled with record Ptr : My_Object_Implementation_Ptr; ... end record; With this scheme, the Initialize procedure would allocate initial values for the pointers, the Finalize procedure would deallocate the pointers, and the Adjust procedure would make independent copies of the pointer data. All of this works fine, as long as no object is assigned to itself. If self assignment does the finalization / adjust sequence, however, this scheme falls apart. For example: x : My_Object; ... x := x; If the assignment statement invokes Finalization on x, we will have destroyed the data in both the source and the target in the assignment. This is not a problem that can be fixed in the Adjust phase; by then, the data is gone. The object is now ruined, in an operation that should have had no effect on x. What if we used a reference count? We would still have a problem in the common case where x points to something with a reference count of 1. In this case, Finalize would decrement the reference count, and finding a reference count of 0, deallocate x.Ptr. Once again, our effort to clean up the target of the assignment ends up destroying the source. Fortunately, on many platforms, self assignment is not an issue. Ada RM 7.6(19) allows the following implementation permission in regards to the assignment of controlled objects: - For an assignment_statement that assigns to an object the value of that same object, the implementation need not do anything. ObjectAda as well as some (all?) versions of GNAT take advantage of this permission. I would recommend that this permission be promoted to a requirement. In other words, we should require that the compiler NOT do the Finalize / Adjust calls when an object is assigned to itself. The issue here is not so much efficiency as it is correctness. If we simply eliminate the Finalize / Adjust calls from self assignments, these problems go away.