From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.5-pre1 (2020-06-20) on ip-172-31-74-118.ec2.internal X-Spam-Level: X-Spam-Status: No, score=-1.9 required=3.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.5-pre1 Date: 14 Sep 93 03:15:17 GMT From: cis.ohio-state.edu!math.ohio-state.edu!howland.reston.ans.net!agate!dog.e e.lbl.gov!network.ucsd.edu!news.cerf.net!shrike.irvine.com!adam@ucbvax.Berkeley .EDU (Adam Beneschan) Subject: Re: Ada 9X: Eliminating the re-emergence of predefined operations Message-ID: List-Id: In article <1993Sep14.022914@lglsun.epfl.ch> madmats@lglsun.epfl.ch (Mats Weber) writes: > This is a very technical discussion. Skip it if you don't like them. > > In Ada 83, the way generic subprogram parameters are passed to > instantiations can make hidden predefined operations of a generic actual > type reemerge unexpectedly. For example, consider the following piece of > code: > > package P is > type T is range 1 .. 23; > function "*" (Left, Right : T) return T; > -- redefine to do modular arithmetic or > -- some other weird thing. > end P; > > generic > type T is range <>; > package GP is > > function Predefined_Mult (Left, Right : T) return T renames "*"; > > end GP; > > package IP is new GP(T => P.T); > > As "*" is redefined in the package P, it seems that the predefined "*" > for type P.T is hidden forever. But this is not true: it reappears inside > the instance IP, and IP.Predefined_Mult denotes the previously hidden > predefined multiplication operator of the type P.T (yes, such are the > rules of Ada 83). It seems to me that this can be worked around by doing the following. However, I'm having trouble determining from the RM whether this is true. generic type T is range <>; with function "*" (Left, Right : T) return T is <>; package GP is function Predefined_Mult (Left, Right : T) return T renames "*"; end GP; Now, it seems that Predefined_Mult will rename the user-defined "*" (to do modular arithmetic or whatever) instead of the predefined "*". Is this correct? Please help. > [deleted] > It is possible that the above change reduces the opportunity to share > code among generic instances, but it is felt much more important to have > solid ADTs than small executables. Unfortunately, in the real world (particularly in the embedded world), executable size does matter to many users. > Moreover, situations where the code > will not be as easily shareable are the seldom cases where, for instance, > "+" has been redefined for an integer type, which deserves special > treatment anyway. This is correct only if the Ada compiler decides at *instantiation* time whether to make the instantiation share code with other instantiations. This may be a significantly more difficult way to implement generics than other methods. If this isn't the case, you pay a penalty ANYTIME you declare a generic, EVEN IF you never, ever redefine any predefined operators. For example, suppose the compiler decides, when the generic formal is compiled, that the generic is to be a shared-code generic. Now, without the rule in 12.3(15), any time a generic subprogram is called, the caller must pass to it a table containing subprogram addresses for *all* the predefined operators that may apply to the type; and the generic subprogram must make an indirect call through that table any time a predefined operator is encountered in the subprogram body. Clearly this is a significant waste of time, but it's necessary because you can't tell at the time the generic is compiled whether it will be instantiated with any types for which the predefined operators have been redefined. The only alternative is not to share the code at all, possibly wasting space. Actually, there might also be a readability advantage to requiring the "with" clause. It alerts the reader to the fact that if she/he sees the operation used within the body of the generic may not mean what she/he thinks it means. generic | generic type T is range <>; | type T is range <>; | with function "+" (left, right : T) | return T is <>; procedure GP (x : in T); | procedure GP (x : in T); | procedure GP is | procedure GP is begin | begin . . . | . . . y := x + 1; | y := x + 1; . . . | . . . end GP; | end GP; In the example on the left, it looks like you're just adding 1 to a number, but on the right, it's clear that "+" is an *abstract* operation that may be redefined for the particular type. Personally, I'd probably go even further and write the code like this: generic type T is range <>; with function ADD (left, right : T) return T is "+"; procedure GP (x : in T); procedure GP is begin . . . y := ADD (x, 1); . . . end GP; just to make it even clearer what's going on. Anyway, this is all my humble opinion. I agree that the rule in 12.3(15) is a "gotcha" that may confuse someone who doesn't know the rules. -- Adam