Version 1.4 of ais/ai-00193.txt
!standard 07.06.01 (04) 99-04-15 AI95-00193/04
!standard 07.06.01 (16)
!class binding interpretation 97-08-19
!status ARG approved (with editiorial changes) 6-0-3 99-03-25
!status work item 98-04-02
!status received 97-08-19
!priority High
!difficulty Hard
!subject Classwide Adjust and exceptions
!summary
For a controlled object (including a component) that is initialized
explicitly by assignment (possibly to an enclosing object), if its Adjust
procedure is invoked but then fails by propagating an exception,
it is not specified by the language whether the object is finalized.
If the object is initialized by assignment from an aggregate, its
Adjust procedure is not invoked (per AI-00083 and AI-00197), but it is
finalized if and only if the initialization is successful.
If it is initialized by assignment from something other
than an aggregate, but its Adjust procedure is not invoked at all
because initialization fails before that point, then the object is
not finalized.
For an object that is initialized by default, the object is not
finalized unless default initialization completes successfully, i.e.
without propagating an exception.
For an Adjust invoked as part of initialization, if it propagates
an exception, no other adjustments need be performed prior to
raising Program_Error.
!question
If an object that is initialized by assignment fails during
an Adjust operation, should the object nevertheless be finalized?
(This AI recommends that this be unspecified.)
!recommendation
Paragraph 7.6.1(4) says:
... each object ... is finalized if the object was successfully
initialized and still exists.
This is relaxed for objects that are initialized by
assignment when an Adjust propagates an exception. For such
objects, the object may be finalized so long as the Adjust operation
is invoked, even if it propagates an exception. It must be finalized
if the Adjust operation completes successfully.
For objects which are initialized by default, no change in the
wording is proposed; such objects are finalized if and only if
default initialization completes without propagating an exception.
The definition of "adjustments due to be performed" should be
relaxed for an assignment operation that is part of initialization,
thereby allowing initialization to be abandoned as soon as any
Adjust fails. For an assignment statement, it is important
that all adjustments be performed, even if one fails, because
all controlled subcomponents are going to be finalized.
!wording
!discussion
When an object (including a component) is initialized by an assignment
other than from an aggregate, the Adjust operation is invoked.
If this operation propagates an exception, then other Adjust operations
that are already due to be performed are performed, and then Program_Error
is raised.
What this means is that if you have a composite object which is
initialized by an assignment from something other than an aggregate,
and it has multiple controlled parts, then if one of the Adjust
operations fails, the others are still invoked. Clearly those parts for
which Adjust succeeds should be finalized per 7.6.1(4). However,
7.6.1(4) implies that the ones for which Adjust fails should not be
finalized. However, for implementations that "bundle" all of the Adjust
operations for all controlled parts of a composite type into a single
"adjust-whole-object" procedure, it is burdensome to keep track
of which parts failed and which succeeded, and only finalize those
whose Adjust succeeded. Note that if some of the Adjust operations
had failed in an assignment statement, all parts would ultimately
still be finalized when the scope is exited and the master is left.
One of the important goals of the finalization model with respect to
exceptions (paragraphs 7.6.1(14-18)) is that if one controlled
abstraction fails by raising an exception in Adjust or
Finalize, this failure should not spread to other unrelated controlled
abstractions. Even when one composite object happens to have
two controlled parts, one from the "failed" abstraction and one
from the "still-good" abstraction, the "still-good" abstraction should
still have Adjust and Finalize called the appropriate number of times
to keep reference counts in sync, avoid dangling pointers, etc.
Given this goal, the "bundling" of Adjust operations, and the
correspondence with assignment statements, it seems best to
allow that, so long as the Adjust routine has been invoked on an
object being initialized, Finalize may be invoked on the object.
On a somewhat separate issue, the notion of adjustments
"due to be performed" (7.6.1(16)) need not apply to initialization
by assignment. So long as a subcomponent is not going to be
finalized, it need not be adjusted, even if it is initialized
as part of an enclosing composite assignment operation for
which some adjustments are performed. On the other hand,
for an assignment that is part of an assignment statement,
it is important that all adjustments be attempted, even if some
of them fail, since all subcomponents are going to be finalized.
This relaxation for adjustments that occur during initialization
means that an initialization may be abandoned as soon as any
Adjust fails, so long as those components which have never been
adjusted are not finalized.
!appendix
!section 7.6.1(4)
!subject Classwide Adjust and exceptions
!reference RM95 7.6.1(4,16)
!from Tucker Taft 97-07-25
!reference 1997-15759.a Tucker Taft 1997-7-25>>
!discussion
To support (explicit) initialization and assignment of classwide
objects, our compiler generates an "Adjust_Whole_Object" routine for every
non-limited tagged type. We dispatch to the appropriate one based
on the tag of the classwide value being adjusted. It now appears we
need to create two "Adjust_Whole_Object" routines, one for use in
initialization, and one for use in assignment. Before we launch off on that
additional work, I wanted to confirm my RM interpretation with others.
BACKGROUND
The Adjust_Whole_Object routine the compiler generates for a tagged type
calls the user-defined Adjust routine of each controlled part of the type
in the appropriate order, handles any exception and sets a flag
but keeps going, and then at the very end, if the exception flag
is set, raises Program_Error. The Adjust_Whole_Object routine
is called with aborts deferred, either as part of initializing
a classwide object by copy, or as part of a classwide assignment statement.
In the case of initialization, we "register" the whole object for
finalization (we have a Finalize_Whole_Object routine which will
clean it up) only if the Adjust_Whole_Object succeeds.
THE PROBLEM
The problem is, if the Adjust_Whole_Object propagates Program_Error,
then we don't register the whole object for finalization. We also
don't register any part of the object for finalization, even
those parts that were successfully adjusted, since we don't have
any record of which parts succeeded versus which parts failed.
This seems to violate the requirement of 7.6.1(4) which says
that any object that is successfully initialized must be finalized.
There seems to be no problem with Adjust_Whole_Object for the
purposes of an assignment statement, because there is no need
to keep track of which parts failed. All the parts have, presumably,
already been registered for cleanup.
The straightforward solution to this problem is to have a separate
Adjust_Whole_Object_For_Initialization routine for each tagged
type, which rather than just raising Program_Error when one or more
parts fail to adjust, will also finalize any parts that were successfully
adjusted before raising Program_Error.
THE QUESTION
Is there some way to have a single Adjust_Whole_Object routine
per tagged type? One possibility is to go ahead and register
the whole object for finalization if at least one part succeeds,
and then live with the fact that we are going to finalize parts
whose Adjust failed. That could happen as a result of an assignment
statement anyway, and the only thing being corrupted is a part
that was already "hosed" to begin with.
Another possibility is to leave things as we have them now, which
means no part is finalized if any parts fail. The problem with that
is that one messed up controlled abstraction can have the effect
of progressively messing up any other controlled abstraction, just
by both being used as components of the same object.
The "finalize-all-if-any-succeeds" therefore seems better to me than
"finalize-none-if-any-fails" approach, but of course neither
matches 7.6.1(4) which says "finalize-only-what-succeeds".
In the spirit of AI95-179, the ARG might say that exceptions inside
Adjust or Finalize are bad news anyway, and we should permit some
relaxation of 7.6.1(4) in one direction or the other.
Alternatively, the ARG says that the independence of controlled abstractions
is quite important, and we should minimize failure coupling between
them even if they appear as components of the same object, and
hence stick with the exact 7.6.1(4) approach.
Comments?
-Tuck
P.S. I think the ada-comment midnight forwarding is fixed,
but I CC'ed "arg95" just in case. -T
****************************************************************
!section 7.6.1(4)
!subject Classwide Adjust and exceptions
!reference RM95 7.6.1(4,16)
!reference 1997-15759.a
!from Bob Duff
!reference 1997-15761.a Robert A Duff 1997-7-28>>
!discussion
Coincidentally, at Rational we recently came across the same issue. We
came to the conclusion that if some subcomponent's Adjust fails, we
should finalize the whole record, including the failed component. This
seems better than skipping some finalizations.
The reason for all these complicated rules about exceptions during
adjust/finalize (C++ is vastly simpler, for the compiler writer, in this
regard, by the way) is so that one broken abstraction can't "infect"
another abstraction. In the case we're talking about here, one
subcomponent is broken, and it doesn't really matter whether we finalize
it or not -- the compiler has no way of knowing whether the Adjust
failed before or after doing something that requires cleanup. Or in the
middle. The programmer of each abstraction really has to either put an
exception handler inside Adjust, or else prove that exceptions can't
happen. Once Program_Error comes flying out of Adjust, there's really
no way to tell what went wrong.
It seems to me that if we're going to stick to this principle, we should
stick to it. No point in saying, well, in a few cases, one broken
abstraction can infect another. It's like saying there's just a small
hole in the dike. On the other hand, it's OK for the broken abstraction
to get more broken -- it's OK for Finalize to be called when
Adjust-during-initialization failed.
Having two Adjust_Whole_Object procedures is possible, but it seems like
added compiler complexity (and code-space inefficiency) for no benefit
to the user. Note that it's not easy for the
Adjust_Whole_Object_For_Initialization procedure to keep track of which
Adjusts were successful -- any subset of them might fail, so it has to
keep a bit for each adjustable subcomponent (it's not like
initialization, where the first failure is all that has to be recorded).
But those might be buried in discriminant-dependent arrays -- it's not
clear how many bits are needed, nor where to allocate them. Surely
we're not going to require allocating them in the object itself, just
for this obscure case!
On the other hand, if we're going to relax the principle to make the
compiler's life easier, we shouldn't try to keep the hole in the dike
small. We should instead say that an exception propagated by
Initialize, Finalize, or Adjust is erroneous. Or we could say that it
aborts the whole program, although there's no precedent for that kind of
rule.
Summary: We should say one of:
(Big hole) any exception in these procedures is erroneous.
(Or some other suitably disastrous consequence, allowing the
compiler to relax.)
or
(No hole) when Adjust fails, all other pending adjusts are done, and
then P_E is raised, causing all the successfully adjusted ones to be
finalized. In addition, it is implementation dependent whether the
unsuccessfully-adjusted ones also get finalized.
We should NOT choose:
(Small hole): When Adjust fails for one subcomponent, this might
cause Finalize for other subcomponents to be skipped.
or
(No hole, but overly complex): Finalize exactly those for whom
Adjust succeeded.
> The problem is, if the Adjust_Whole_Object propagates Program_Error,
> then we don't register the whole object for finalization. We also
> don't register any part of the object for finalization, even
> those parts that were successfully adjusted, since we don't have
> any record of which parts succeeded versus which parts failed.
This seems bad.
> The straightforward solution to this problem is to have a separate
^^^^^^^^^^^^^^^
> Adjust_Whole_Object_For_Initialization routine for each tagged
> type, which rather than just raising Program_Error when one or more
> parts fail to adjust, will also finalize any parts that were successfully
> adjusted before raising Program_Error.
That seems like a misuse of the term "straightforward", to me. ;-)
> Is there some way to have a single Adjust_Whole_Object routine
> per tagged type?
I've thought of some, but they all seem even *worse*.
>... One possibility is to go ahead and register
> the whole object for finalization if at least one part succeeds,
> and then live with the fact that we are going to finalize parts
> whose Adjust failed.
That seems like the right solution, to me (assuming we want to stick
with the "no infection" principle).
>... That could happen as a result of an assignment
> statement anyway, and the only thing being corrupted is a part
> that was already "hosed" to begin with.
Right.
> Another possibility is to leave things as we have them now, which
> means no part is finalized if any parts fail. The problem with that
> is that one messed up controlled abstraction can have the effect
> of progressively messing up any other controlled abstraction, just
> by both being used as components of the same object.
Agreed -- I don't like that solution.
> The "finalize-all-if-any-succeeds" therefore seems better to me than
> "finalize-none-if-any-fails" approach, but of course neither
> matches 7.6.1(4) which says "finalize-only-what-succeeds".
Agreed.
> In the spirit of AI95-179, the ARG might say that exceptions inside
> Adjust or Finalize are bad news anyway, and we should permit some
> relaxation of 7.6.1(4) in one direction or the other.
>
> Alternatively, the ARG says that the independence of controlled abstractions
> is quite important, and we should minimize failure coupling between
> them even if they appear as components of the same object, and
> hence stick with the exact 7.6.1(4) approach.
But we can relax 7.6.1(4) in *one* direction without causing failure
coupling.
- Bob
****************************************************************
!section 7.6.1(4)
!subject Classwide Adjust and exceptions
!reference RM95 7.6.1(4,16)
!reference 1997-15759.a
!from Bob Duff
!reference 1997-15762.a Robert A Duff 1997-7-28>>
!discussion
> The problem with that is that of course, if Adjust propagates an
> exception, later Adjusts won't get done. Thus there is a minor
> violation of the rules as written.
It seems OK to skip some pending Adjusts, so long as the corresponding
Finalizes don't happen.
Actually, I wonder if the implementation permissions in 7.6(18-21), as
extended by AI95-147 (which is not yet approved), already allow this?
The AI says:
Matching pairs of Adjust/Finalize and Initialize/Finalize may be
eliminated by the implementation. A variable that is never otherwise
referenced can be eliminated, even if its Initialize or Finalize has
side effects.
Since the subcomponent in question will not be further referenced, why
can't we eliminate the Adjust/Finalize of it?
> In this case, however, that can only be detected by a pathological
> program which is counting the number of Adjust calls. (Like,
> unfortunately, most validation tests).
I believe many of those validation tests are wrong.
- Bob
****************************************************************
!section 7.6.1(4)
!subject Classwide Adjust and exceptions
!reference RM95 7.6.1(4,16)
!reference Tucker Taft 97-07-25
!from Randy Brukardt 97-07-25
!reference 1997-15760.a Randy Brukardt 1997-7-25>>
!discussion
>The problem is, if the Adjust_Whole_Object propagates Program_Error,
>then we don't register the whole object for finalization. We also
>don't register any part of the object for finalization, even
>those parts that were successfully adjusted, since we don't have
>any record of which parts succeeded versus which parts failed.
>This seems to violate the requirement of 7.6.1(4) which says
>that any object that is successfully initialized must be finalized.
>Is there some way to have a single Adjust_Whole_Object routine
>per tagged type?
No. I gave up on that quite a while back. However, see below.
>One possibility is to go ahead and register
>the whole object for finalization if at least one part succeeds,
>and then live with the fact that we are going to finalize parts
>whose Adjust failed. That could happen as a result of an assignment
>statement anyway, and the only thing being corrupted is a part
>that was already "hosed" to begin with.
>Another possibility is to leave things as we have them now, which
>means no part is finalized if any parts fail. The problem with that
>is that one messed up controlled abstraction can have the effect
>of progressively messing up any other controlled abstraction, just
>by both being used as components of the same object.
>In the spirit of AI95-179, the ARG might say that exceptions inside
>Adjust or Finalize are bad news anyway, and we should permit some
>relaxation of 7.6.1(4) in one direction or the other.
There is a third approach here, which I think avoids all of the problems, but still
works usefully for this case.
Our compiler registers each component immediately after it is assigned and
adjusted successfully. (Note that regular assignment works very much as
Tucker describes it; I'm only talking about initialization assignments or "explicit
initializations" here.) The problem with that is that of course, if Adjust propagates
an exception, later Adjusts won't get done. Thus there is a minor violation of the
rules as written.
In this case, however, that can only be detected by a pathological program which
is counting the number of Adjust calls. (Like, unfortunately, most validation tests).
That's because if initialization propagates an exception, the object is immediately
finalized. (In our case, that happens automatically with the regular finalization
mechanism.)
Therefore, I recommend relaxing the monolithic Adjust for initialization assignments.
Sibling components that the monolithic Adjust would handle (but my proposal would
not) cannot be accessed anyway (as the object will be immediately destroyed), so
they do not do anything useful. Parent components that the monolithic Adjust
would handle (but my proposal would not), in addition, would be saved the potential
problem of dealing with trashed components with failed Adjusts.
Note that the reason for the monolithic Adjust on regular assignments (that the
object will be accessible afterwards, so we need to do as many Adjusts as we can)
does not apply in the initialization assignment case.
Therefore, I propose changing 7.6.1(16) to read:
For an Adjust invoked as part of an assignment_statement, any other adjustments
due to be performed are performed, and then Program_Error is raised; for an Adjust
invoked as part of some other assignment operation, Program_Error is raised.
We could also just word this as a permission. "any other adjustments due to be
performed may be performed".
Randy.
****************************************************************
!section 7.6.1(4)
!subject Classwide Adjust and exceptions
!reference RM95 7.6.1(4,16)
!reference 1997-15759.a Tucker Taft
!reference 1997-15762.a Bob Duff
!from Randy Brukardt
!reference 1997-15764.a Randy Brukardt 1997-7-28>>
!discussion
>> The problem with that is that of course, if Adjust propagates an
>> exception, later Adjusts won't get done. Thus there is a minor
>> violation of the rules as written.
>It seems OK to skip some pending Adjusts, so long as the corresponding
>Finalizes don't happen.
>Actually, I wonder if the implementation permissions in 7.6(18-21), as
>extended by AI95-147 (which is not yet approved), already allow this?
>The AI says:
>
> Matching pairs of Adjust/Finalize and Initialize/Finalize may be
> eliminated by the implementation. A variable that is never otherwise
> referenced can be eliminated, even if its Initialize or Finalize has
> side effects.
>
>Since the subcomponent in question will not be further referenced, why
>can't we eliminate the Adjust/Finalize of it?
I think this logic is correct. Therefore, we don't even need a rule change to be
allowed to implement this sensibly. Certainly, my approach is a lot easier to
implement than the one suggested by Tucker, and it does not contain any hole.
(If it did, there is a problem with AI-147).
BTW, I have not received any of this discussion via the comment repeater, so I
assume it is still not working. Has anyone else??
****************************************************************
!section 7.6.1(4)
!subject Classwide Adjust and exceptions
!reference RM95 7.6.1(4,16)
!reference 1997-15759.a Tucker Taft
!reference 1997-15762.a Bob Duff
!reference 1997-15764.a Randy Brukardt
!from Tucker Taft 97-07-28
!reference 1997-15765.a Tucker Taft 1997-7-28>>
!discussion
> ...
> I think this logic is correct. Therefore, we don't even need a rule change
> to be
> allowed to implement this sensibly. Certainly, my approach is a lot easier to
> implement than the one suggested by Tucker, and it does not contain any hole.
> (If it did, there is a problem with AI-147).
Your approach, where you raise the exception immediately, only works because
you register as you go. This probably isn't what is wanted for the assignment
statement, so your approach still requires separate routines per tagged
type for Adjust-for-initialize, and Adjust-for-assignment-statement.
That's not the end of the world, as far as I am concerned, but if
I could use just one that would be simpler for our implementation.
(And save the user some code space and compile-time.)
If we are allowed to Finalize fields that fail during Adjust, then
I could use a single routine that would raise one of two different
exceptions, one special one meaning that there was at least one success,
and any other meaning that there was no successful Adjust.
> BTW, I have not received any of this discussion via the comment repeater, so I
> assume it is still not working. Has anyone else??
As I mentioned in a separate note, you should now be receiving
comments each midnight.
-Tuck
****************************************************************
!section 7.6.1(4)
!subject Classwide Adjust and exceptions
!reference RM95 7.6.1(4,16)
!reference 1997-15759.a Tucker Taft
!reference 1997-15762.a Bob Duff
!reference 1997-15764.a Randy Brukardt
!from Bob Duff
!reference 1997-15768.a Robert A Duff 1997-7-28>>
!discussion
> Your approach, where you raise the exception immediately, only works because
> you register as you go. This probably isn't what is wanted for the assignment
> statement, so your approach still requires separate routines per tagged
> type for Adjust-for-initialize, and Adjust-for-assignment-statement.
True, but at least the Adjust-for-initialize routine only needs to
record a single failure, not an arbitrary set of failures (for various
subcomponents of discriminant-dependent arrays and so forth!). Once the
failure occurs, it finalizes any previously-adjusted parts, and then
gives up.
> That's not the end of the world, as far as I am concerned, but if
> I could use just one that would be simpler for our implementation.
> (And save the user some code space and compile-time.)
Agreed -- a single whole-Adjust routine is simpler, and more efficient,
than two.
> If we are allowed to Finalize fields that fail during Adjust, then
> I could use a single routine that would raise one of two different
> exceptions, one special one meaning that there was at least one success,
> and any other meaning that there was no successful Adjust.
I don't understand what you mean, here. I see one exception --
Program_Error -- why do you see two? If P_E_ is raised, the
whole-object will get finalized, and if that contains some parts that
failed, tough luck (under that "interpretation").
- Bob
****************************************************************
!section 7.6.1(4)
!subject Classwide Adjust and exceptions
!reference RM95 7.6.1(4,16)
!reference 1997-15759.a Tucker Taft
!reference 1997-15762.a Bob Duff
!reference 1997-15764.a Randy Brukardt
!reference 1997-15765.a Tucker Taft
!from Randy Brukardt 97-07-29
!reference 1997-15772.a Randy Brukardt 1997-7-30>>
!discussion
>Your approach, where you raise the exception immediately, only works because
>you register as you go. This probably isn't what is wanted for the assignment
>statement, so your approach still requires separate routines per tagged
>type for Adjust-for-initialize, and Adjust-for-assignment-statement.
True, but I think it is a red herring. I think you agree that you have to generate
separate code for initialize-assignment and assignment-statement. (Aside: We didn't
used to do that, using a single thunk, but the Ada 95 rules make that just too
messy, so we now generate two thunks.)
An Adjust-whole-object routine is necessary in order to provide the proper semantics;
that is, to insure that all Adjusts get done if any raise an exception. However,
the proposed Adjust/Register pairing does not have this property. It can be in-lined
as part of the assignment code, either as part of an init-assign thunk (as we do),
or directly in-line. Therefore, this approach does NOT require an additional routine
(of course, you're always free to build as many routines as you want. :-). It does
take a bit of additional code space, but getting the correct semantics in Ada
often does that (over the way you'd do it if you didn't have to follow those silly rules. :-)
I doubt it takes as much code as handling Unchecked_Deallocation properly, for
example.
I cannot support relaxing the rules any further in this area. My other hand is involved
in constructing a significant interface (CLAW) which depends heavily on Initialize /
Adjust / Finalize semantics. Our experience with the permissions is that it can be
very hard to determine what exactly is going to happen (especially in portable code).
This is especially true with rules that allow the elimination of Adjusts without
corresponding elimination of Finalize (i.e. 7.6.1(21), last sentence). I bet most users
of controlled types will be surprised to find that they have to include at least one
aliased component before they can trust that common techniques like reference
counting will work. (Personally, I thunk users will be happiest if compiler writers
ignore that permission - I certainly intend to).
From an abstraction viewpoint, it is important that no extra Finalizes be called on
my abstraction unless Initialize or Adjust has successfully completed. As the
constructor of the abstraction, I know that MY Initialize, Adjust, and Finalize cannot
propagate an exception. (Of course, during testing, this assumption gets violated a
lot!). The property I want to insure is that the failure of someone else's Initialize or
Adjust does not cause my Finalize to either be skipped or called extra times.
(Note: Finalize, of course, is constructed to allow calling multiple times. We do that
with a flag "Active", which is set to False during a Finalize call. If "Active" is False,
Finalize does nothing. However, that does not help for a Adjust which is not called,
because the components of the source object were copied in, resetting "Active" flag.
Therefore, it is imperative that extra Finalizes not be generated on items which
have not be Adjusted.
Randy.
****************************************************************
From: Robert A Duff[SMTP:bobduff@world.std.com]
Sent: Thursday, April 09, 1998 7:20 PM
Subject: Re: AI-00193/1
> !standard 07.06.01 (04) 98-04-09 AI95-00193/01
...
> still be finalized when the scope is existed.
^^^^^^^^^^^^^^^^
You misspelt "master is left". ;-)
> Hence, in the default initialization case, if the Adjust operation of
> the first controlled component fails, the initialization of the second
> controlled component can be skipped completely, and only the
^^^^^^
"can be" should be "is", assuming you mean "first" wrt the order of
initialization chosen by the impl. Or did you mean wrt textual order of
component decls?
- Bob
****************************************************************
From: Tucker Taft[SMTP:stt@inmet.com]
Sent: Friday, April 10, 1998 10:38 AM
To: arg95@sw-eng.falls-church.va.us
Subject: Re: AI-00193/1
A few new thoughts on the issue of "adjust operations due to be performed":
> ...
> As a point of clarification, adjustments "due to be performed"
> are all those associated with a single assignment operation.
> If a composite object is default initialized, and it has
> two controlled components each of which has its own
> explicit initialization expression, the adjustment of the
> second controlled component is not "due to be performed" just because
> the first controlled component's adjust operation has been
> invoked. By contrast, if such a composite object is initialized
> as a whole by assignment, then the adjust operations of all of its
> controlled parts are "due to be performed" as soon as the bytes
> representing the source (right-hand-side) of the assignment operation
> are copied on to the composite object being initialized.
> ...
I'm not sure I buy this rule any more. There seems no harm in
stopping after the first failing Adjust operation in the case of
an assignment operation used as part of initialization, so long
as only parts for which Adjust has been invoked are in fact finalized.
In an assignment statement, the whole object is eventually going
to be finalized, so clearly you want to go ahead and do all the
Adjusts for parts that have been copied onto, even if one of them fails.
For initialization, it seems permissible to conceptuallly do component by
component assignment/adjustment, even if the implementation starts
by doing a block copy into the uninitialized storage, so long
as parts that have not been adjusted are not finalized.
So all in all, my attempt to "clarify" what is meant by
"adjustments due to be performed" should probably be ignored.
There is perhaps something we could say to clarify the
intent of "due to be performed," but I am too lazy to figure
it out right now. Maybe "do the right thing" ;-).
-Tuck
****************************************************************
From: Gary Dismukes[SMTP:dismukes@gnat.com]
Sent: Friday, April 10, 1998 1:51 PM
Subject: Re: AI-00193/1
> I'm not sure I buy this rule any more. There seems no harm in
> stopping after the first failing Adjust operation in the case of
> an assignment operation used as part of initialization, so long
> as only parts for which Adjust has been invoked are in fact finalized.
I agree, we shouldn't require all components to be adjusted on
init-by-assignment. That seems like overspecification and could
have an undesirable impact on some existing implementations.
****************************************************************
From: Randy Brukardt[SMTP:Randy@rrsoftware.com]
Sent: Friday, April 10, 1998 3:37 PM
Subject: RE: AI-00193/1
Tucker: Thanks for noticing that the AI goes too far. That saves me having
to figure it out how to explain it.
I think it is important that we not force existing implementations to
change here (unless they want to, or if they are actually wrong.) This is
a failure case, and the important issue is to write the rules so that the
failure of one abstraction doesn't kill an unrelated one. We don't want or
need to over-specify the behavior beyond that needed to insure that.
As noted previously in my mail on the subject (attached to the AI), it is
reasonably possible to implement the correct semantics (taking into account
AI-147 permissions) without much additional code. While an extra
permission which allows even less code to be used is attractive, it
certainly should not be made into a requirement --the improvement in code
size may not be significant for many applications.
****************************************************************
From: Tucker Taft[SMTP:stt@inmet.com]
Sent: Friday, April 10, 1998 3:12 PM
Subject: Re: AI-00193/1
Gary Dismukes writes:
> I agree, we shouldn't require all components to be adjusted on
> init-by-assignment. That seems like overspecification and could
> have an undesirable impact on some existing implementations.
Yes. I think the key requirements are:
1) A part of an object must be finalized if and only if it has
been either successfully default initialized, or, as part of
initialization-by-assignment, been adjusted (successfully or not).
2) During an assignment statement, once a part of the LHS object
is overwritten with a copy of the corresponding part of the
right-hand-side, that part must be adjusted.
Presuming these requirements are satisfied, I don't see any
need for the notion of "adjustments due to be performed" during
initialization by assignment.
During an assignment *statement*, the "adjustments due to be performed"
are defined by requirement (2), namely once some part of the LHS object
has been overwritten, adjustment is "due to be performed" on that part
of the LHS object.
-Tuck
****************************************************************
Questions? Ask the ACAA Technical Agent