======================================================================== 1 === Date: Sun, 2 Feb 92 19:36:59 PST From: muller@src.dec.com (Eric Muller) Subject: archive of comp.lang.modula3 for January 1992 The archive of the messages sent to comp.lang.modula3 in January 1992 is now available in: gatekeeper.dec.com:pub/DEC/Modula-3/comp.lang.modula3/92-01.Z Achives for the previous months are available in the same directory. Eric. ======================================================================== 2 === Date: Tue, 4 Feb 1992 15:34:37 PST From: David Goldberg Subject: M3 tags package for GNU Emacs I have put the tags package we use at PARC on parcftp@xerox.com, in /pub/goldberg/tags.tar.Z. Its available using anonymous ftp. Here's the README file from tags.tar: ================ This is a tags package for Modula-3 and GNU emacs. It consists of a 'tags' program that prints a tags file on stdout, and an improved version of tags.el. See the comments in tags.el and tags.c for more info. As an example of how to use these programs, you might create a tags file via these commands (where INSTALL_PREFIX is the location of the installed Modula-3) cd INSTALL_PREFIX tags -q include/m3/*.i{3,g} > INSTALL_PREFIX/FTAGS and invoke the tags package with these lines in your .emacs file: (load-file "tags.el") (visit-tags-table "INSTALL_PREFIX/FTAGS") David Goldberg goldberg@parc.xerox.com Marvin Theimer theimer@parc.xerox.com ======================================================================== 3 === Date: 4 Feb 92 21:48:38 GMT From: do12+@andrew.cmu.edu (Dick Orgass) Subject: Beta build of SRC Modula-3 2.01 for RISC/6000 Available A quite usable but not completely shaken down build of SRC Modula-3 2.01 and all of the released libraries is available for anonymous ftp from emsworth.andrew.cmu.edu [128.2.30.62] as file m3/binary.IBMR2-1.01.tar.Z. I'd appreciate it if others would try the build and let me know about problems that are found so there is a chance of fixing them before the final release from SRC. This build is done under the assumption that it is installed in /u/decsrc. If you install it in another place, just add a symbolic link from /u/decsrc to that place. Alternatively, you could rebuild the driver. Comments about this build: (1) The system has passed the ptests and etests. (2) I haven't run the Pkl test programs yet and they caused some problems with 1.6. (3) The trestle demo BadBricks stops with an invalid instruction (sigh!). (4) The libraries are built as ordinary libraries -- I haven't modified the imake macros to make share libraries as was done form 1.6. (5) The compiles were done without -O and with -g so the library files are very much bigger than they should be. (6) There is a file, m3compiler.AIX386 in directory lib/m3 which you can delete. It's a cross compiler for AIX PS/2 that didn't help me solve a problem. Dick ======================================================================== 4 === Date: 5 Feb 92 23:14:49 GMT From: d9mikael@dtek.chalmers.se (Mikael Wahlgren) Subject: Re: Modula-3 fans, read comp.lang.modula-3! (Was: Re: Modula-3 for PC) In article <1992Feb5.085953.2347@cs.rug.nl> laverman@cs.rug.nl (Bert Laverman) writes: >Now as a repeat: The SRC-m3 compiler (current version ca. 2.01) is a >C generating compiler for several _UNIX_ machines. The library with it >supports several _UNIX_ variants and systems. _It_does_not_support_MSDOS_ >nor does it support the 80386 (the most likely processor for a first >80x86 implementation) > If you have a PC compiler for m3, or know of somebody having one, >NOTIFY US (the comp.lang.modula3 readers) IMMEDIATELY! We don't know >of it. Well, I have had thoughts on porting the SRC-m3 compiler to OS/2 2.0, but I don't know if that counts (and if it is relevant). It certainly runs on "PC's", but you can run Unix on PC's too... Anyone interested in a Modula-3 port to OS/2 2.0 (32-bit operating system). This is not a commitment to do a port, just wanting to know if there is any interest... Mikael Wahlgren d9mikael@dtek.chalmers.se ======================================================================== 5 === Date: 6 Feb 92 22:33:56 GMT From: harbison@bert.pinecreek.com (Sam Harbison) Subject: A UNIX shell in Modula-3 David Hanson and David Dobkin of Princeton University kindly gave me a copy of their "small shell", a small UNIX shell written in C which they use as an example program in some of their courses. I have finished translating/rewriting it into Modula-3, and it is now available for anonymous ftp from bert.pinecreek.com as file pub/lib/m3shell-1.0.tar.Z. It won't supplant the Korn shell, but it handles command execution, I/O redirection, pipes, and background execution. For example: (cd /tmp; ls -lt | cat) >> thud & It doesn't handle quotes or filename expansion. The program is intended as an interesting example of how to hack UNIX tastefully in Modula-3. In giving it a Modula-3 flavor, I've split the single C file into several modules, put in a couple of objects, and changed the error handling scheme to use exceptions. The program includes an interface and module named M3Unix, which provides a Modula-3 flavored interface to several UNIX system calls (i.e., the ones I needed). Enjoy. Sam Harbison Pine Creek Software; Suite 300; 305 South Craig Street; Pittsburgh, PA 15213; USA. Phone&FAX: +1 412 681 9811. E-mail: harbison@bert.pinecreek.com. ======================================================================== 6 === Date: Thu, 6 Feb 92 21:57:46 PST From: muller@src.dec.com (Eric Muller) Subject: SRC Modula-3 2.03 available SRC Modula-3 2.03 is available from gatekeeper.dec.com in pub/DEC/Modula-3/m3-2.0. This version has been successfully installed on a DECstation and on a VAX. I believe that the SPARC version is ok, and probably the SUN3 as well. The status for the other machines is unknown. To get started, you need to fetch the archive boot.-2.03.tar.Z and libm3-2.03.tar.Z. Because of the reorganization for ports, you cannot really reuse a 2.01 installation. Main changes since 2.01: - it should be easy to do ports of this version; well, the machinery to build a cross-compiler and to cross compile the driver and the compiler are there (you still have to modify the machine-specific code). The bootstrap archives are now built using that mechanism. - David Goldberg provided the implementation of the floating point interfaces for SPARC. Thanks a lot. - missing setjmp.o added for IBMR2 and AP3000 - a number of bug fixes in the compiler. - the driver in -make mode does the correct dependency analyzis for generic interfaces and modules. - command line arguments for the runtime. We have reserved command line arguments of the form @M3 for the runtime. Before the user code starts, these arguments are removed from the list seen by the user code, and stored in a safe place. The interface RTParams can be used to access them. can be just a , or of the form =. Two such arguments are used today: @M3showthread and @M3showheap. - showthread: if a Modula-3 program is started with the runtime argument @M3showthread=, this will fork a process and run the program (or showthread if = is missing) in that child process. That program should close its stdout to allow the parent process to proceed and will then receive on stdin a sequence of thread scheduling events for the parent process. The tools source archive contains the program showthread, that uses Trestle to display this scheduling activity. - showheap: if a Modula-3 program is linked with the ShowHeap interface (available in the tools source archive) and Trestle, and the @M3showheap runtime argument is given, this will open a Trestle window in which the status of the heap will be displayed. There are also some buttons in that window to control the allocator/collector. Note: the code to handle that window is in the process being examined, contrary to showthread which is in a separate process. We are going to convert showheap to be in a separate process so that the Trestle activity does not interfer with the program, and you don't need to link differently. -- Eric. ======================================================================== 7 === Date: 7 Feb 92 02:03:00 GMT From: v064qpfu@ubvmsb.cc.buffalo.edu (Christopher S. D'Arrigo) Subject: MODULA2 This questions deal with Modula 2, I know the news group is based on Modula 3 but since I couldnt find a Modula 2 News Group, I figured this was my best shot.... I have JUST started using Modula 2 and was wondering... How do you evaluate Exponents, if Num^Power is a pointer to Power and Num ** Power is an invalid operand, what is the correct way to code for exponents... Thanks! ************************************************************************* * All Things Considered, I'd Rather Be Doing COBOL * My Opinions are my * *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=* own so they must * * IN%"V064qpfu@UBVMS", IN%"cdarrigo@cs.Buffalo.edu"* be right! -CED * ************************************************************************* ======================================================================== 8 === Date: 7 Feb 92 05:46:47 GMT From: NU158739@NDSUVM1.BITNET (Shaun Wetzstein) Subject: Re: Modula-3 fans, read comp.lang.modula-3! (Was: Re: Modula-3 for PC) I would like to see Modula-3 for OS/2 v2.0! Shaun Wetzstein nu158739@vm1.nodak.edu ======================================================================== 9 === Date: Sat, 8 Feb 92 00:06:43 PST From: muller@src.dec.com (Eric Muller) Subject: Re: patch for m3-2.03 In article <1992Feb7.235931.29769@src.dec.com>, I write: > There is a little problem with the top level m3makefile of all the > archives (this is actually the same file in all cases). I forgot to mention that this problem and the fix have been found by Man-Chi Pong, from The Hong Kong University of Science & Technology. Thanks a lot ! -- Eric. ======================================================================== 10 === Date: Fri, 7 Feb 92 23:59:31 PST From: muller@src.dec.com (Eric Muller) Subject: patch for m3-2.03 There is a little problem with the top level m3makefile of all the archives (this is actually the same file in all cases). Below is the difference between the old and the new version. This modification is necessary for SPARC (at least). *** m3makefile.old Fri Feb 7 23:52:18 1992 --- m3makefile Fri Feb 7 23:53:15 1992 *************** *** 145,146 **** --- 145,147 ---- #define machine_iter(x) @ @\ + all_##x:: @ @\ @for d in $(MACHINES); do \ @@\ *************** *** 149,158 **** ! all_cross:: ! machine_iter (cross) ! all_bootstrap_both:: ! machine_iter (bootstrap_both) ! all_bootstrap_scratch:: ! machine_iter (bootstrap_scratch) ! all_pack:: ! machine_iter (pack) --- 150,155 ---- ! machine_iter (cross) ! machine_iter (bootstrap_both) ! machine_iter (bootstrap_scratch) ! machine_iter (pack) -- Eric. ======================================================================== 11 === Date: 7 Feb 92 01:23:59 GMT From: torki@doitcr.doit.sub.org (Torsten Kirschner) Subject: Q: Does M3 version 2.X exist/work on DECstation 5000/200 ? Dear M3 - group, I have not been following the M3 version 2.x postings, but as I read about IBM RS/6000 ports I ask You whether the implementation for the DECstation 5000 series works ? I assume the new version's tar.Z can be ftp'ed from gatekeeper.dec.com, but I'd like to hear about Your experience, especially with the Trestle Window System which is kind of troublesome with M3 1.6 (as Greg Nelson himself said). Thanks in advance for any hints & tips. (I use DS5000/200 w/Ultrix 4.2 and DECwindows 11R4, if that has anything to do with it.) Torsten ======================================================================== 12 === Date: 8 Feb 92 15:41:34 GMT From: harbison@bert.pinecreek.com (Sam Harbison) Subject: Modula-3 News #2 correction In the article on Queen Mary & Westfield College in "Modula-3 News" #2, I referred to Jean Dollimore as 'he'; it should have been 'she'. My sincere apologies to Ms. Dollimore. Sam Harbison Pine Creek Software; Suite 300; 305 South Craig Street; Pittsburgh, PA 15213; USA. Phone&FAX: +1 412 681 9811. E-mail: harbison@bert.pinecreek.com. ======================================================================== 13 === Date: Mon, 10 Feb 92 14:54:21 PST From: muller@src.dec.com (Eric Muller) Subject: Re: Q: Does M3 version 2.X exist/work on DECstation 5000/200 ? In article <1737@doitcr.doit.sub.org>, torki@doitcr.doit.sub.org (Torsten Kirsc hner) writes: > I have not been following the M3 version 2.x postings, but as I read > about IBM RS/6000 ports I ask You whether the implementation for > the DECstation 5000 series works ? The quality of the various architecture is a consequence of: - the number of users of that architecture - the similarity of that architecture DS3100 DS3100 (i.e. DECstation running Ultrix 4.2) is the architecture on which we develop. We have a fair number of users at SRC. It is certainly the one that works the best. VAX comes next, as we have one of those machines, and we try to test it from time to time (we don't have many VAX users, so it may be behind the DS3100 version). We don't have any of the other architectures available to us, so we have to rely on good willing people outside. They are putting a fair amount of work to make Modula-3 a reality, thanks to them all ! Sometime, their task is greatly complicated by differences between their architecture and the DS3100 architecture that we did not anticipate. Sometime, they have a small number of users and it is difficult to test thoroughly the system. By all means, do not equate quality of the port with quality of the people doing it. -- Eric. ======================================================================== 14 === Date: Wed, 12 Feb 92 17:51:36 HKT From: mcpong%uxmail.ust.hk@YALEVM.YCC.Yale.Edu Subject: Trestle scrollbar? Is there any MODULE scrollbarVBT available in Trestle? Any pre-release version is better than nothing. Thanks mcpong@uxmail.ust.hk Man-Chi Pong The Hong Kong University of Science & Technology ======================================================================== 15 === Date: Wed, 12 Feb 1992 11:08:53 PST From: David Nichols Subject: pragma question The SRC implementation restricts the appearance of certain pragmas, e.g. it is an error for <*UNUSED*> to appear other than just before a declaration. But the green book, on page 62, states "Pragmas are hints to the implementation; they do not affect the language semantics." It seems to me that refusing to compile an otherwise correct program is affecting the language semantics. What if GNU M3 decides to give a different meaning to <*UNUSED*>, or requires a different placement? Then I wouldn't be able to write code that uses <*UNUSED*> and is portable between the two compilers. David ======================================================================== 16 === Date: 12 Feb 92 18:10:21 GMT From: cs224054@cs.brown.edu (Manojit Sarbar) Subject: Re: Trestle scrollbar? In article <9202120951.AA13394@ustsu1.ust.hk.ust.hk1> mcpong%uxmail.ust.hk@YALE VM.YCC.Yale.Edu writes: >Is there any MODULE scrollbarVBT available in Trestle? I did some programming with Trestle. There is no ScrollarVBT so far as I know, I had to write my own. -manojit ======================================================================== 17 === Date: Wed, 12 Feb 92 09:57:59 PST From: Subject: Re: Trestle scrollbar? We at SRC have developed a large collection of UI components. We are using them in a handful of heavily-used applications, so they are pretty solid --- by no means "vaporwear". We are currently working on the reference manual, as well as some tweaking, tuning, enhancing, and plain ol' fashion debugging. To give you a feel for the types of widgets that will soon be available in Trestle, here's a list of the primary VBT classes: BooleanVBT ChoiceVBT FileBrowserVBT FlexVBT GuardedBtnVBT NumericVBT PixmapVBT ReactivityVBT ScrollerVBT ShadowedBarVBT ShadowedVBT SourceVBT SplitterVBT TextBrowserVBT TextEditVBT TrillBtnVBT TypescriptVBT ViewportVBT ZChassisVBT Hopefully, the names will give a clue as to the functionality you can expect. Marc PS. The components are the building blocks of a GUI system called FormsVBT. As posted to this newsgroup in December, we have a "mini" version of that system mostly running in Modula-3, but it's a few months before FormsVBT will be ready for distribution. Stay tuned.... ======================================================================== 18 === Date: 13 Feb 92 14:54:37 GMT From: moss@cs.umass.edu (Eliot Moss) Subject: Re: pragma question I tend to agree that the whole area of pragmas needs work. How can I as a compiler writer choose a pargma name guaranteed not to conflict with any other compiler's? I also suspect that it would be best to require (suppressable) warning for unrecognized/misplaced pragmas, so a programmer at least knows that a compiler did not process the pragma. And saying that a pragma may not affect semantics may be too limiting; not to mention the effect you noticed: a pragma affecting the acceptance/rejection of a program by the compiler. (One could argue that this does *not* affect semantics, since the semantics of an unaccepted program are not defined.) We haven't implemented any pragmas for gm3 yet, but I'm sure there will be a day .... Eliot -- J. Eliot B. Moss, Assistant Professor Department of Computer Science Lederle Graduate Research Center University of Massachusetts Amherst, MA 01003 (413) 545-4206, 545-1249 (fax); Moss@cs.umass.edu ======================================================================== 19 === Date: 13 Feb 92 09:16:35 From: dagenais@vlsi.polymtl.ca (Michel Dagenais) Subject: Re: Trestle In article <9202121758.AA20698@jumbo.pa.dec.com> mhb (Marc H. Brown) writes: > To give you a feel for the types of widgets that will soon be available > in Trestle, here's a list of the primary VBT classes: > > BooleanVBT ChoiceVBT FileBrowserVBT FlexVBT > GuardedBtnVBT NumericVBT PixmapVBT ReactivityVBT > ScrollerVBT ShadowedBarVBT ShadowedVBT SourceVBT > SplitterVBT TextBrowserVBT TextEditVBT TrillBtnVBT > TypescriptVBT ViewportVBT ZChassisVBT > Humm... hard to survey what is there and what is not when things are moving at this pace. Not that i will complain for all the good stuff coming out! Here is a more basic question about the architecture of Trestle. In InterViews (a C++ windowing toolkit) Glyphs (read VBT) are "shared" objects. Thus, a Glyph that gets used by numerous other composite Glyphs, mapped to a number of windows, will need to keep information about all these contexts (for each context remember the glue settings and so on). This contextual information can be kept in a hash table and the calling path, which determines the exact context, can serve as the key. Therefore, the Glyphs can represent directly complex objects (drawings or documents) with multiple viewers. In Trestle, from what i could gather in the comments sread around the files, the VBTs are not shared. Therefore, there is a one to one correspondance between the display and the VBTs. Thus they act as display lists. If one wants to implement a diagram or document editor, he then needs to create new VBTs for each instance of a component (NAND gate for example) in each viewer. When this component is modified, each VBT instance representing it must receive an update message and react accordingly. Am i correct? In which area do you feel that Trestle needs more functionality? Do you know enough about InterViews to comment on what exists in it and doesnt in Trestle, and vice-versa? -- --------------------------------------------------------------------- Prof. Michel Dagenais dagenais@vlsi.polymtl.ca Dept of Electrical and Computer Eng. Ecole Polytechnique de Montreal tel: (514) 340-4029 --------------------------------------------------------------------- ======================================================================== 20 === Date: 13 Feb 92 22:46:35 GMT From: n8243274@henson.cc.wwu.edu (steven l. odegard) Subject: Re: pragma question May I recommend packaging pragmas much like modules are packaged now. Thus if a particular pragma set is used, ie. for formal verification, then the same is imported in a like manner. I am working on some extended pragma names for documenting incremental extension. I don't look forward to having to select names other than the intuitive set I use. By the way, in each case, the form is <* KEYWORD other stuff *>, where _other stuff_ is handled by KEYWORD. The position of where the pragma appears is also significant, in some cases. ======================================================================== 21 === Date: 13 Feb 92 21:54:21 GMT From: emery@Dr_No.mitre.org (David Emery) Subject: Re: pragma question Eliot writes: >How can I as a compiler writer choose a pargma name guaranteed not to >conflict with any other compiler's? I'd suggest that DEC, or whoever maintains the Modula-3 Reference Manual, could maintain a 'registry' of pragma names and meanings. In the Ada community, there are two ISO WG9 groups that deal with this. The ARG handles questions of conformance to the standard, such as "Can I add an extra parameter to this pragma?". The URG handles uniformity issues. One URG issue deals with pragma INTERFACE_NAME, which in Ada terms associates an external object (or subprogram) denoted by pragma INTERFACE with its external (i.e. linker) name. Other URG issues deal with things like the "normal" size of INTEGER, and similar issues that are not legality issues, but are important for portability. Modula-3 could probably use a similar group, to catalog these issues, if not decide them. dave ======================================================================== 22 === Date: Thu, 13 Feb 92 20:02:11 GMT From: torki@doitcr.doit.sub.org (Torsten Kirschner) Subject: Re: Q: Does M3 version 2.X exist/work on DECstation 5000/200 ? In article <1992Feb10.145421.6112@src.dec.com> you write: } In article <1737@doitcr.doit.sub.org>, torki@doitcr.doit.sub.org (Torsten Kir schner) writes: } } > I have not been following the M3 version 2.x postings, but as I read } > about IBM RS/6000 ports I ask You whether the implementation for } > the DECstation 5000 series works ? } } The quality of the various architecture is a consequence of: } - the number of users of that architecture } - the similarity of that architecture DS3100 } } DS3100 (i.e. DECstation running Ultrix 4.2) is the architecture on } which we develop. We have a fair number of users at SRC. It is } certainly the one that works the best. } } VAX comes next, as we have one of those machines, and we try to test } it from time to time (we don't have many VAX users, so it may be } behind the DS3100 version). } } We don't have any of the other architectures available to us, so we } have to rely on good willing people outside. They are putting a fair } amount of work to make Modula-3 a reality, thanks to them all ! } Sometime, their task is greatly complicated by differences between } their architecture and the DS3100 architecture that we did not } anticipate. Sometime, they have a small number of users and it is } difficult to test thoroughly the system. By all means, do not equate } quality of the port with quality of the people doing it. } } -- } Eric. Dear Eric, thank You for Your quick and well sufficient response to my inquiry. As a matter of fact one has to thank the good poeple at SRC for their work. I am using M3 1.6 with Greg Nelson's Trestle system. All I can say quickly is that I am truly impressed by M3 and prefer it to any other language available on my system. Due to Your information I will ftp the latest version of M3 from gatekeeper.dec.com. I think it is 2.03 by now, but never mind to correct me if it's 2.04 already. Not that it was of any interest to You, but my reason for posting the questions about M3 2.x on DECstations was that ftp-ing for me as a X.400 user is complicated. In addition to that bandwidth to the USA is limited and I have not found a mirror of gatekeeper as far as M3 is concerned. No European ftp server seems to have M3 versions >1.6. I would be glad and thankful if You and everybody involved with Modula 3 kept on their precious work. I am looking forward to a time when I can contribute something, even if it was just a little hint about the dis- tribution. Thanks again Torsten -- Torsten R. Kirschner Tel. (+49 89) 3234102 BITNET: torki@dm0mpf11 SMTP : torki@doitcr.doit.sub.org torki@mpipf-muenchen.mpg.dbp.de X.400: /C=de/ADMD=dbp/PRMD=mpg/O=mpipf-muenchen/S=torki ======================================================================== 23 === Date: 14 Feb 92 15:05:18 GMT From: harbison@bert.pinecreek.com (Sam Harbison) Subject: bert.pinecreek.com anon FTP down Until further notice, anonymous ftp to bert.pinecreek.com is unavailble. It will likely be down for weeks or months. The file system holding that account became corrupted last night, and I don't have a readily available backup. This happened while building a Modula-3 cross compiler on that disk. Hmmm, could it be...? Nah... Sam Harbison Pine Creek Software; Suite 300; 305 South Craig Street; Pittsburgh, PA 15213; USA. Phone&FAX: +1 412 681 9811. E-mail: harbison@bert.pinecreek.com. ======================================================================== 24 === Date: 15 Feb 92 16:50:23 GMT From: n8243274@henson.cc.wwu.edu (steven l. odegard) Subject: bug in m3-2.03: procedure declaration within a block Should every error found on the Beta Test be reported here? MODULE Bug EXPORTS Main ; (* Demonstrate that if a I declare a procedure in a declaration within a block, then compiler will produces a C syntax error. Here I declared such a procedure _FinalFive_. *) IMPORT Wr, Fmt, Stdio ; TYPE funny = ARRAY[ 1 .. 5 ] OF INTEGER ; PROCEDURE FunnyFive( f : funny ) = (* This procedure causes no error. *) BEGIN FOR k := 1 TO 5 DO Wr.PutText( Stdio.stdout, "f[" & Fmt.Int( k ) & "] = " & Fmt.Int( f[k] ) & " " ) ; END (* OF FOR k *) ; Wr.PutText( Stdio.stdout, "\n" ) ; Wr.Flush( Stdio.stdout ) ; END FunnyFive ; BEGIN (* --- FunnyFive( funny{ 5, 4, 3, 2, 1 } ) ; FunnyFive( funny{ -1, .. } ) ; --- *) TYPE final = ARRAY[ 1 .. 5 ] OF INTEGER ; PROCEDURE FinalFive( f : final ) = (* This procedure causes a C error. *) BEGIN FOR k := 1 TO 5 DO Wr.PutText( Stdio.stdout, "f[" & Fmt.Int( k ) & "] = " & Fmt.Int( f[ k ] ) & " " ) ; END (* OF FOR k *) ; Wr.PutText( Stdio.stdout, "\n" ) ; Wr.Flush( Stdio.stdout ) ; END FinalFive ; BEGIN (* scope for _FinalFive_ 'test' procedure with _final_ type. *) FinalFive( final{ 5, 4, 3, 2, 1 } ) ; FinalFive( final{ -1, .. } ) ; END (* OF BEGIN (* scope for _FinalFive_ *) *) ; (* Output should be: --- f[1] = 5 f[2] = 4 f[3] = 3 f[4] = 2 f[5] = 1 f[1] = -1 f[2] = -1 f[3] = -1 f[4] = -1 f[5] = -1 --- *) END Bug. (* --- g> m3 bug.m3 ccom: Error: bug_m.c, line 39: syntax error void 1__FinalFive (); -------------^ ccom: Error: bug.m3, line 26: syntax error void 1__FinalFive (f) _t91633b98 f; { _RAISES_NONE_HANDLER _h6; --------------^ ccom: Error: bug.m3, line 26: syntax error { _h6.next = *((_TRY_HANDLER **) &_M3__handlers); _M3__handlers = 0 ; }; --^ ccom: Error: bug.m3, line 26: redeclaration of _h6 . . . (* You may generate the other 30 or so errors at your leisure. *) --- *) ======================================================================== 25 === Date: Mon, 17 Feb 1992 23:58:14 GMT From: sharon@lcs.mit.edu (Sharon Perl) Subject: the Fmt interface Is there a particularly good reason why Fmt.FN should result in a checked runtime error when the number of format specifiers in the format string is not equal to the number of supplied texts? I'd prefer that it raised an exception that could be caught by the caller. I wanted to use Fmt.FN in writing a simple expression interpreter. The user of the interpreter provides a format string and a list of expressions to be evaluated (by the interpreter) to produce the texts for the format string. I'd like to pass the format string supplied by the user directly to Fmt.FN along with the array of texts resulting from evaluating the expressions, without having to first parse the format string myself to check whether the number of texts is correct. I'd like to just catch an exception from Fmt.FN and provide a sensible error message for the user when the format string is incorrect. In general, it seems to me that checked runtime errors should only result from errors that are most definitely programming errors, and for which the only thing the caller could possibly do in catching an exception is print an error message and terminate the program. With Fmt.FN this is not the case. Note that the <* FATAL *> pragma makes it easy to instruct the compiler that particular errors should in effect be checked runtime errors, but there's no way to reverse the situation and "catch" a checked runtime error. Sharon Perl MIT Lab for Computer Science sharon@lcs.mit.edu ======================================================================== 26 === Date: 18 Feb 92 18:30:19 GMT From: sharon@lcs.mit.edu (Sharon Perl) Subject: Re: the Fmt interface Sam Harbison writes: Yeah! Yeah! (thumping table) *All* checked run-time errors should be mapped to exceptions. I don't want some guys in Palo Alto deciding what's fatal to MY program! (A more reasoned argument is available upon request.) Actually, CLU did this and it seems to work just fine. CLU has a special exception called "failure" which has a single text argument. All routines can raise "failure" in addition to any explictly listed exceptions in their interfaces. An unhandled exception in a routine is converted to "failure". So, for example, an array bounds error causes the "bounds" exception to be raised. If the caller of the array operation does not catch "bounds", the result is that the caller raises "failure". But "failure" can be caught with an exception handler just like any other exception. I haven't thought this through, but it seems that something like this could work in Modula-3. It might be different than CLU's mechanism since M3 doesn't have the uniformity that CLU does in its treatment of operations on built-in types compared with user-defined procedures. Is there a reason why this wouldn't work? Sharon Perl MIT Lab for Computer Science sharon@lcs.mit.edu ======================================================================== 27 === Date: 19 Feb 92 17:33:08 GMT From: moss@cs.umass.edu (Eliot Moss) Subject: Re: the Fmt interface No, I see no reason why mapping checked run-time errors to exceptions won't work. It is, however, more difficult to implement since some checked run-time exceptions are turned into Unix signals and it is much easier to print a message and abort than fiddle with the running state so that returning from the signal handler will work as desired. And it's probably hard to do in a single portable way, too. Finally, if signal handlers are used, they do not always allow the handler to distinguish the reason for the signal as well as one would like, especially if it has to do with various numeric problems. Still, I tend to support the idea. Are there other compelling reasons for the the design's being the way it is? -- J. Eliot B. Moss, Assistant Professor Department of Computer Science Lederle Graduate Research Center University of Massachusetts Amherst, MA 01003 (413) 545-4206, 545-1249 (fax); Moss@cs.umass.edu ======================================================================== 28 === Date: 19 Feb 92 12:49:25 GMT From: qs101@cl.cam.ac.uk (Quentin Stafford-Fraser) Subject: Problems installing for HPUX I have been trying to build m3 v2.03 for HP-UX 7.0. The various makefiles etc took quite some tweaking, but I eventually got an 'm3' binary after 'm3make build_boot install_boot' Unfortunately, any attempt to use this produces: unrecognized option '-z4' usage: m3 [-?] [options] [-o pgm|-a lib|-c] sources... objs... libs... Fatal Error: bad usage Can anybody help ? Please? ------------------------------------------------------------------------ Quentin Stafford-Fraser "I've no idea what my employer Cambridge University Computer Lab thinks about this." qs101@cl.cam.ac.uk Tel +44 223 334645 ------------------------------------------------------------------------ ======================================================================== 29 === Date: 19 Feb 92 13:15:38 From: dagenais@vlsi.polymtl.ca (Michel Dagenais) Subject: Compiling Trestle demo applications I have not been able to find a README that explains how to build and install the programs in the directory modula3-SRC-2.03/trestle/apps. The "m3make build_trestle install_trestle" command does not build these sample trestle applications. I tried "cd trestle/apps; m3make all install" but this did not get very far on my SparcStation 1. Before i go and compose a suitable m3makefile for this directory, is there something i have overlooked? The rest of the build/install for SRC 2.03 just went fine. By the way, if there is enough interest i could put up for ftp the sparc binaries for Modula 3 SRC 2.03... send e-mail if this is all you need to start using Modula 3 :-). -- --------------------------------------------------------------------- Prof. Michel Dagenais dagenais@vlsi.polymtl.ca Dept of Electrical and Computer Eng. Ecole Polytechnique de Montreal tel: (514) 340-4029 --------------------------------------------------------------------- ======================================================================== 30 === Date: Thu, 20 Feb 92 07:11:54 PST From: msm@src.dec.com (Mark S. Manasse) Subject: Re: Compiling Trestle demo applications The apps subdirectory of trestle is supposed to build some test programs, and not install any of them. Other than BadBricks, I don't think that anything it builds is terribly useful, except to test that Trestle and M3 are working, and as examples of how to write simple Trestle programs. Mark ======================================================================== 31 === Date: Thu, 20 Feb 92 15:49:59 PST From: Subject: Re: the Fmt interface There are two different issues running through this discussion and I think they must discussed separately. The first is a style question brought up by Sam Harbison: What should the result of a procedure be when it has been called incorrectly by a client. Should it raise a specific exception, signal a runtime error, or return an erroneous answer? Note that the Larch/Modula3 (LM3) specification language is capable of specifying any combination of these behaviors. In general returning erroneous answers is not great, especially when the requirements that the client is expected to meet are difficult for the client to verify. But sometimes the performance cost of verifying the condition is so great that one wants to avoid the checking. The locking level requirements of Trestle and of the Thread interface are good examples. Note that a given implementation might generate a checked runtime error even if the specification allows erroneous results. For example, Thread.Release in M3 generates a checked runtime error if it is called by the thread that is not holding the mutex even though the specification allows chaos. What about raising a separate exception for each thing a client could do wrong? On one extreme, consider a client that passed in NIL to a procedure that needs a non-NIL object. Should there be a special exception listed in the RAISES clause for this? I've seen some procedures in Modula 2+ with 15 exceptions on the RAISES clause! I don't like using procedures like that. On the other hand, the client could pass in a noninvertible matrix into a matrix invert routine. An exception or return code, would be much nicer than a checked runtime error, even if there was a separate CheckInvertible routine. In the end the decision of how errors should be reported depends on the abstraction designers and their model of what clients want. I don't believe there can be a general rule. If the designers of your favorite interfaces get it wrong, tell them! Of course, if too many clients get into the act you end up with a mess of too many options and exceptions and nobody is happy. The second issue is whether all checked runtime errors should be turned into special exceptions (with standard names or not) that may be caught explicitly or by the ELSE clause of a TRY EXCEPT. We actually did this to our Modula 2+ system: - The only place where we took advantage of it was in the Tinylisp, (a Lisp/Modula 2+ hybrid) debugger. This debugger lived inside your Modula 2+ program and would take control if one of the threads got a checked runtime error. It could then display the stack, examine variables and destroy or restart the thread. It was necessarily in bed with the exception machinery and the the runtime architecture. - We didn't do it much, but if one did want to simply mask bugs by using TRY EXCEPT ELSE one would at least want to log a stack trace or the exception name somewhere. I would argue that since TRY EXCEPT ELSE is not currently defined to catch runtime errors and has no place to get a parameter, that if we do decide to implement runtime errors as exceptions that we invent a new Failure exception that could be caught. The argument to Failure would hopefully be a TEXT containing something suitable for logging. - Masking programming bugs has a way of turning a fail-stop program into a byzantine one. Fail stop programs are much easier to deal with. I'm not sure I would want my program to continue after a bug without my having determined what went wrong. This was one reason why programmers did not knowingly use TRY EXCEPT ELSE to catch errors. (They sometimes did so unknowingly when feeling lazy when dealing with procedures that raise 15 different exceptions.) - It takes more mechanism to report an error. For example, a Unix signal comes in and has to be directed into the right thread and that thread has to be made to execute the proper exception handling code. We had cases where bugs have messed up the exception delivery machinery and errors have gotten disguised. Garret Swart ======================================================================== 32 === Date: Thu, 20 Feb 92 13:38:41 PST From: Subject: Safe cheap objects for Modula 3 This message outlines an idea to allow the compiler to allocate certain objects on the stack and/or combine several objects and allocate them as one unit, saving allocator and garbage collection overhead. This technique allows us to gain the advantages of C++'s stack resident objects without any of the inherent unsafety that they have in C++. The idea is to introduce a new pragma that gives the compiler more information about object lifetimes and thus allows it to allocate objects more efficiently. First we introduce a new parameter passing mode, called USE, a restriction of Modula 3's default "value" mode, for use on REF and OBJECT types. A value passed by USE can only be narrowed, have its fields accessed, be dereferenced (if it is a REF type) or be passed as a USE mode parameter. It is thus impossible for a recipient of a USE mode parameter to retain a handle to the object after its return. USE can be the passing mode for "self" in object methods, however as the Modula-3 object syntax does not mention "self" in the method signature we use the convention that <*USE*> after the method name but before the parameter list refers to the passing mode of "self". With the extra information provided by USE, it would then be the compilers option to note that NEW was called in a context where the result was only used as a USE mode parameter and allocate the object on the stack. For example given Goo.T INTERFACE Goo; TYPE T <: OBJECT METHODS init <*USE*> (value: INTEGER); inc <*USE*>(<*USE*> by: T) END: END Goo. both instances below can be allocated on the stack WITH g = NEW(Goo.T), h = NEW(Goo.T) DO g.init(7); h.init(3); g.inc(h); END; because the compiler can determine that the objects created by the two calls to NEW are never used past the end of the WITH. I contend that had USE been part of the language from day one, USE would actually be the default, or possibly the only (!), mode for passing the "self" parameter of an object method. Wide use of USE mode implies that the convention of having an "init" method return self or a "new" procedure or method that allocates the object, is bad. Following this convention means that objects of those types cannot be allocated on the stack, or as we will see later, combined with other objects to save allocations. Greg agrees with this and he plans on not using the convention of returning the object in the init method anymore. He doesn't plan to fix Trestle because instances of its main type, VBT.T, are pretty heavy weight and are not used in a way that the optimization applies. Note that I expect USE mode parameters will be used primarily on object types. A parameter tagged USE REF X is very similar to VAR X, and VAR X is useful in more settings. Since an opaque REF type (<: REFANY) cannot be allocated, the usefulness of USE with those types is quite limited. For these reasons I would have no objection if USE could only be applied to object types. Further note the similarity of the above to an idiom that can already used in Modula 3 to allow the compiler to allocate the array a on the stack: WITH a = NEW(REF ARRAY OF X, n) DO Munge(a^); END; The M3 compiler doesn't actually try to take advantage of this. USE can also used to mark a field of an Object or a REF RECORD. This restricts the field to be assigned only as part of calling NEW on the containing object. After being set this field can only have its fields accessed, be dereferenced (if it is a REF type) or be passed as a USE parameter. If the field itself is initialized with a call to NEW then we know that the life time of the object referred to by the field is exactly the same as the object the field is contained in. This allows the compiler to allocate the objects as a single unit. Note that this works whether the parent object is being allocated on the stack or the heap. For example given INTERFACE Foo; TYPE T = OBJECT <*USE*> g1, g2: Goo.T; END; END Foo. and x := NEW(Foo.T, g1 := NEW(Goo.T), g2 := NEW(Goo.T)) then the compiler may allocate the Foo.T and the two instances of Goo.T as a single unit on the heap. The two optimizations may be combined as in WITH f = NEW(Foo.T, g1 := NEW(Goo.T), g2 := NEW(Goo.T)) DO f.g1.init(7); f.g2.init(3); f.g1.inc(f.g2); END; where all three objects may be allocated together on the stack. To make this technique useful for opaque object types we actually have to extend the language to allow one to give a more general default field initializer. Currently default field initializers are limited to constant expression. The minimal extension is to allow this to be a call to NEW whose parameters are themselves constant expressions or similar calls to NEW. For example in: INTERFACE Foo; TYPE T <: OBJECT METHODS init <*USE*> (); END; END Foo. rather than have Foo.init allocate the two fields g1 and g2, which would not be possible if those fields were marked as USE, we can allow all the objects to be allocated in one unit by allowing IMPLEMENTATION Foo; REVEAL T = BRANDED OBJECT <*USE*> g1, g2: Goo.T := NEW(Goo.T); METHODS init <*USE*> (); END; END Foo. This allows a client of Foo to allocate a Foo.T and have its two associated Goo.T's allocated along with it as one unit. Having a NEW loop, e.g. if Goo.T has a field that is initialized as a NEW(Foo.T), is a checked runtime error. Note that the same NEW loop bug can occur now with init functions, resulting in a stack overflow or an infinite loop. Note that with this technique we have managed to separate one reasons for subtyping, avoiding extra allocations, from the more important reason, that of data abstraction. As Greg says on page 168 of his book with reference to readers and writers: To achieve this pattern of information-hiding without partially opaque object types, it would be necessary to allocate each group of fields separately and link them together with additional references. This would require several allocations per writer, which would be costly. We now have another technique to avoid allocations without the restrictions of fitting each set of fields into a strict type hierarchy. Unless there is a storm of protest, as part of the next round of interface changes that we have planed here at SRC I would like to change important "lightweight" objects (e.g. readers and writers, condition variables and mutexes, others??) to follow the "no return value init method" convention and to use the <*USE*> pragma. This next round of interface changes may not be visible to the outside for some time but it may have bearing on your design choices for your own objects. Garret Swart ======================================================================== 33 === Date: Thu, 20 Feb 92 14:41:31 PST From: muller@src.dec.com (Eric Muller) Subject: Floating point interfaces SPwM3, p74: GENERIC INTERFACE Float (R); TYPE T = R.T; This generic interface provides access to the floating-point operations required or recommended by the IEEE floating-point standard [ANSI/IEEE Std 754-1985]. Consult the standard for the precise specifications of the procedures, including when their arguments are NaNs, infinities, and signed zeros, ... So far so good, but: 1. The only mention of Logb is in the Appendix of the standard, and the first line of the Appendix says: (This Appendix is not a part of ANSI/IEEE Std 754-1985, IEEE Standard for Binary Floating-Point Arithmetic.) 2. More seriously, the Appendix does not tell anything about ILogb. I am trying to find what ILogb (NaN), ILogb (Inf) and ILogb (+/-0) should be. SPwM3 says of ILogb: Like Logb, but returns an integer, never raises .... On the other hand, the Appendix says: Logb(Nan) is a NaN, logb (Inf) is +Inf, and logb (0) is -Inf. What is an integer that is like a NaN ? like +Inf ? like -Inf ? -- Eric. ======================================================================== 34 === Date: Fri, 21 Feb 1992 00:53:02 GMT From: treese@crl.dec.com (Win Treese) Subject: Harbison's Modula-3 Book Quantum Books in Cambridge, MA, now has Sam's book: Newsgroups: misc.books.technical,wstd.quanbook Path: news.crl.dec.com!deccrl!decwrl!uunet!world!quanbook From: quanbook@world.std.com (Quantum Books) Subject: New Release: Modula-3 Message-ID: Organization: The World Public Access UNIX, Brookline, MA Date: Thu, 20 Feb 1992 22:45:50 GMT New Release Modula-3 Samuel P. Harbison (c) 1992, Prentice Hall ISBN 0-13-596396-6 Paperback, 312 pages $30.00 Author Samuel P. Harbison offers readers the first complete guide to writing programs in Modula-3 - the newest member of the Pascal family of languages. Content Highlights include: o A complete discussion of object-oriented programming, including objects and methods, classes, and inheritance o A set of programming conventions to help readers write more consistent and readable Modula-3 programs o The SRC Modula-3 implementation run-time libraries o Modula-3 language quick reference o Concurrent Programming o Modula-3's support for threads and semaphors (from back of book) -- Quantum Books | A Technical and Professional Bookstore ----------------------------+------------------------------------------ Cambridge: 617-494-5042 | E-Mail: quanbook@world.std.com Philadelphia: 215-222-0611 | Mailing List: quanlist@world.std.com ======================================================================== 35 === Date: Thu, 20 Feb 92 22:18:30 PST From: muller@src.dec.com (Eric Muller) Subject: Re: Request mail-pool of volunteer "Experts" In article , mod3sys@gonzo.cc.wwu.edu (modula3 system programmer) writes: > I am beginning to work extensively in SRC Modula-3 v2.03, and herewith > request names and address for anyone willing to answer minor questions, > provide moral support, and so on. I'd be willing to answer any such > questions also: > mod3sys@gonzo.cc.wwu.edu m3-request@src.dec.com is always accepting questions. If you need moral support, think about the poor SRCers who use SRC Modula-3 all day long (if not all night long). -- Eric, 8-) or 8-(, depending on the day. ======================================================================== 36 === Date: 21 Feb 92 01:12:35 GMT From: mod3sys@gonzo.cc.wwu.edu (modula3 system programmer) Subject: Request mail-pool of volunteer "Experts" I am beginning to work extensively in SRC Modula-3 v2.03, and herewith request names and address for anyone willing to answer minor questions, provide moral support, and so on. I'd be willing to answer any such questions also: mod3sys@gonzo.cc.wwu.edu -- S. Lee ODEGARD ======================================================================== 37 === Date: Fri, 21 Feb 1992 18:44:54 PST From: David Goldberg Subject: Re: Floating point interfaces In response to Eric's mail about the value of ILogb: You'd like to have 2^ILogb(x)=x, and for x = infinity, the closest you can come is ILogb(x) = LAST(INTEGER). Similarly, ILogb(0) should be FIRST(INTEGER); There is no reasonable answer you can give for Ilogb(NaN). This brings up what seems to be a bug in the math interfaces, namely they don't have a RAISES clause with FloatMode.Trap. For example, Scalb ought to RAISE Trap(Flag.Overflow) for large values of n. Since ILogb(NaN) can't return NaN, I believe it should raise an exception. Either it should always raise Trap(Flag.Invalid), no matter what FloatMode.behavior is, or else to avoid making a special case for the meaning of Behavior, there should be some new EXCEPTION introduced for Ilogb to RAISE in this case. -david ======================================================================== 38 === Date: Fri, 21 Feb 1992 19:01:28 PST From: David Goldberg Subject: Re: the Fmt interface While the question of whether to turn all runtime errors into EXCEPTIONS has pros and cons, I'd like mention a special case that I think is especially compelling and is fairly easy to implement: an EXCEPTION for memory allocation failures (this has been mentioned on this list before). Here's an example of where this is crucial: Your program builds an in-memory cache for performance, but suddenly some other part of your program starts to do a lot of allocations and runs out of memory. The memory allocation handler is just what you need: It can trim down on the cache size, and then retry the failing part of the code. -david ======================================================================== 39 === Date: 21 Feb 92 14:15:01 GMT From: piet@cs.ruu.nl (Piet van Oostrum) Subject: Re: Problems installing for HPUX >>>>> qs101@cl.cam.ac.uk (Quentin Stafford-Fraser) (QS) writes: QS> I have been trying to build m3 v2.03 for HP-UX 7.0. QS> The various makefiles etc took quite some tweaking, but I eventually got QS> an 'm3' binary after 'm3make build_boot install_boot' QS> Unfortunately, any attempt to use this produces: QS> unrecognized option '-z4' QS> usage: m3 [-?] [options] [-o pgm|-a lib|-c] sources... objs... libs... QS> Fatal Error: bad usage QS> Can anybody help ? Please? I have been working on the port of 2.03 for HP-UX (8.0). I discovered some problems in version 2.01(?), mailed it to the list but I think it got lost somewhere. If you want to have the new list please get in touch with me so I can mail it to you. I completed compiling the compiler, but there are still some problems with libm3. -- Piet* van Oostrum, Dept of Computer Science, Utrecht University, Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands. Telephone: +31 30 531806 Uucp: uunet!mcsun!ruuinf!piet Telefax: +31 30 513791 Internet: piet@cs.ruu.nl (*`Pete') ======================================================================== 40 === Date: Fri, 21 Feb 1992 10:36:19 PST From: Mike_Spreitzer.PARC@xerox.com Subject: Re: Safe cheap objects for Modula 3 I've occasionally wondered about such a feature myself. I'm surprised by the differences between your proposal and what I came up with, and wonder why. Here's what I would have expected. The goal is to enable a compiler to prove that one value does not enable some subsidiary value to outlive the first value. For examples: * For a particular field F of a record type R, where there's a variable r of type R, wherever the field F is extracted from r, the extracted value is dead by the time r is assigned to as a whole or r's scope is exited (which ever comes first). Similarly for elements of arrays. * For a particular type R = REF T, wherever there's a value r of type R and r^ appears, the dereferenced value is dead by the time r must be dead. * For a formal argument A of a procedure P, inside P's body the actual value passed in A is dead by the time P returns (or is left by an exception). (I mean "dead" as in dataflow analysis: a value is dead past the last reference to it in the graph. I've also used the phrase "must be dead", for something closer to the source text: a value "must be dead" past the last place(s) in the source text where a reference could be inserted.) When this kind of statement can be made for every datatype that enables access to one value from another, a compiler can then get the payoff: the ability to prove that a value does not outlive a stack frame. This is useful for allocation in the stack, combining allocations (which actually includes allocation in the stack, if you consider a stack frame as something that's "allocated"), *and* checking that local procedures do not outlive their containing activations. So here's what it would look like for Modula-3. I would use a word more indicative of the discipline that's being indicated. Let's say the pragma is <*STACKED*>. It can appear as an adjective in the following places, with the indicated meanings: * ARRAY [Index] OF [<*STACKED*>] Element Wherever an element is extracted from such an array value, the extracted value does not outlive the point(s) where the array value must be dead. * before a field name in a RECORD fieldlist Wherever that field is extracted from that type of record value, the extracted value is dead by the point(s) where the record value must be dead. * [UNTRACED] REF [<*STACKED*>] Referent Wherever the referent value is extracted from such a reference value, the extracted value is dead by the point(s) where the reference value must be dead. * before a formal parameter name in a procedure or method signature In the body of the procedure or method, the value received through that parameter is dead by the time the body is exited. * after a method name in the Methods section of an object type In the body of the method, "self" is dead by the time the body is exited. * before a field name in an object type Wherever a value is extracted from that field of an object of that type, the extracted value is dead by the point(s) where the object value must be dead. * before a variable name in a VAR declaration No value extracted from this variable will outlive the extracting activation of the block containing this declaration. Note that a compiler could automatically determine which variables are stacked regardless of whether the programmer has declared them so; thus, these uses of <*STACKED*> are not really necessary, and could be dropped from the proposal. Note: * The methods of an object are always stacked: the procedure value of a method cannot be extracted from an object. * Local procedures are always stacked. * It must be possible for the "self" parameter to object methods to be not stacked; sometimes an object needs to insert itself in a data structure. * There is no need to prevent assignments to stacked fields of REF RECORDs, unless we get into garbage collector implementation issues, which I hadn't until reviewing Garret's message; I don't see how assigning to stacked fields of REF RECORDs is more problematic that assigning to stacked arguments to procedures, which Garret doesn't obviously prohibit. Without a prohibition on assignment, there's no need to generalize initialization. * A VAR T parameter is similar to (but *more* restricted than, in the un-modified language) a VALUE <*STACKED*> UNTRACED REF T, modulo the ability in the caller to coerce a variable into such an untraced reference. If the caller were further willing to notice such a call and place the runtime type code in the right place, a VAR T parameter would be subsumed by a VALUE <*STACKED*> REF T. Examples of legality and illegality: TYPE R = RECORD <*STACKED*>f1: T1; <*STACKED*>f2: T2 END; TYPE T1 = ...; TYPE T2 = ...; PROCEDURE P1(r: R; f: T1; x: REF T1) = VAR <*STACKED*>g := r.f1; (* Legal *) BEGIN IF r.f1=x^ THEN RETURN END; x^ := r.f1; (* Illegal *) (* the value passed in r is "dead" here *) IF g=x^ THEN RETURN END; (* g is "dead" here. *) x^ := f; RETURN; (* the value passed in r "must be dead" here. *) END P1; PROCEDURE P2(<*STACKED*>r: R; f: T1; x: REF T1) = ...; PROCEDURE Top(r: R; f1: T1; x: REF T1) = VAR f2a = ...; VAR <*STACKED*>f2b = ...; BEGIN r.f1 := f1; (* Legal *) P1(R{f1:=f1; f2:=f2a}, f1, x); (* Legal *) P1(r, f1, x); (* Legal *) P1(r, r.f1, x); (* Illegal *) P2(r, f1, x); (* Legal *) P2(R{f1:=f1; f2:=f2b}, f1, x); (* Legal *) P1(R{f1:=f1; f2:=f2b}, f1, x); (* Illegal *) RETURN; END Q; Note that even though the T1 value that is extracted from r.f1 in the first statement of P1 or P2 exists in memory after the activation of P, it does not do so (if the illegal statements are ignored) because of its presence in the f1 field, but rather by a different path that does not place <*STACKED*> restrictions. Also, the reason the last call on P1 is illegal is that P1 doesn't guarantee that the value of its "r" parameter doesn't outlive the activation (the <*STACKED*> restriction on field f2 alone isn't enough to guarantee that the value passed to P1 in r.f2 doesn't escape the activation of P1). Here are some examples of usage: PROCEDURE Find(l: List.T; ...): REFANY = PROCEDURE HardCase(r: REFANY): REFANY = ...; PROCEDURE EasyCase(r: REFANY): REFANY = ...; VAR <*STACKED*>TheCase: PROCEDURE (r: REFANY): REFANY; VAR ans: REFANY; BEGIN IF ... THEN TheCase := HardCase ELSE TheCase := EasyCase END; (* Legal because TheCase is stacked. *) WHILE l # NIL DO ans := TheCase(l.first); (* This is he only place a value is extracted from "TheCase"; the value is dead when the called body begins executing. *) IF ans#NIL THEN RETURN ans END; l := l.rest; END; RETURN NIL; END Find; This is just a warmup, and doesn't need any changes (recall that declaring variables stacked is not really necessary), only a smarter compiler (and relaxation of a language restriction). For the next example, refer to section 3.7 (pp 85--87) of SPwM3, and consider a client that wants to call tbl.map: PROC Client(...) = VAR tbl: Table.T; (* ... and lots more local variables *) PROCEDURE Test(key: Key.T; value: Value.T): BOOLEAN = (* a body that references many of Client's local variables *); BEGIN ... tbl.map(NEW(Closure, apply := Test)); (* Would be legal if Table had declared map's cl argument to be <*STACKED*>. *) ... END Client; Suppose that when Client was first written, there was no call on tbl.map. A year later, Client was modified slightly, and the call on tbl.map introduced. Without this proposal, Client would have to be modified more extensively, to move all the local variables of Client accessed by Test into a REF. I could imagine adding a prohibition on assigning into stacked components, to ease GC implementation. I haven't worked it through, so I don't know that's necessary to get reasonable GC implementations. Does the SRC compiler do enough dataflow analysis to enable checking the restrictions I'm suggesting <*STACKED*> could express? If not, I suspect one could get a decent approximation by local restrictions on the uses of a stacked component (in the style Garret suggests, but with more rules). Doesn't it seem right to make this kind of restriction (STACKED or USE) apply to records, arrays, references, and objects in the way I've suggested (ie, meaning nesting of lifetimes) rather than the special case for OBJECT and REF RECORD fields that Garret suggests? Mike ======================================================================== 41 === Date: Fri, 21 Feb 92 09:26:51 PST From: Subject: Re: Safe cheap objects for Modula 3 Garret, I like the idea of <*USE*> very much. In order to allow <*USE*> to apply to fields of objects as well as to parameters, you propose to liberalize the rules for initializing fields at NEW time, allowing some non-constant expressions in field defaults where today only constant expressions are allowed. I think it would be better to leave the rules for initializing fields alone, and allow assignment to <*USE*> mode fields. I don't see much benefit from prohibiting such assignments; the point of <*USE*> is to prevent a reference from escaping into a global, not to prevent it from being modified. You seem inclined to think of <*USE*> only in regard to objects, but I think it would be exciting to change the field declaration buff: REF ARRAY OF CHAR in a stream, replacing it with <*USE*> buff: UNTRACED REF ARRAY OF CHAR after which DISPOSE(stream.buff) will be (or could be) safe! In any case I suspect it is premature to expect to use <*USE*> uniformly in our interfaces. USE is much too interesting to use yet. Greg ======================================================================== 42 === Date: 21 Feb 92 13:30:28 GMT From: paul@ste.dyn.bae.co.uk (Paul Byrne) Subject: Yacc files I'm am trying to locate yacc and lex files for Modula2 and Modula3 syntax. If anyone has a suitable syntax or can direct me to an ftp site please e-mail me. Thanks in advance Paul Byrne ======================================================================== 43 === Date: 21 Feb 92 19:55:53 GMT From: dagenais@vlsi.polymtl.ca (Michel Dagenais) Subject: Sparc binaries of modula 3 SRC-2.03 Since at least one request echoed my offer, i packaged the sparc binaries of Modula 3 SRC-2.03 (with all the libraries). It is now available for ftp from wotan.vlsi.polymtl.ca in pub/modula3-2.03-sparc.tar.Z. When uncompressed it takes 30MB and must be installed (or soft linked) in /usr/local. Then, you can link /usr/local/bin/m3 to /usr/local/modula3-SRC-2.03/run/poly/sun4.0-sparc/bin/m3 and do the same thing for the other binaries and the man pages. The compiler will find the library modules and the include files in their location if you install everything as prescribed. This setup might sound strange but it is the result or a few years experience with a large installation where software packages must run on many platforms, with some locally developed variants and frequent version upgrades. Enjoy and let me know if you have problems, our ftp account is not very active and sometimes gets rusty. -- --------------------------------------------------------------------- Prof. Michel Dagenais dagenais@vlsi.polymtl.ca Dept of Electrical and Computer Eng. Ecole Polytechnique de Montreal tel: (514) 340-4029 --------------------------------------------------------------------- ======================================================================== 44 === Date: Mon, 24 Feb 92 13:05:06 PST From: Subject: Re: Safe cheap objects for Modula 3 This message attempts to clear up some of the problems of understanding that Greg and Mike had with the previous message. I had a good conversation with Bill Kalsow on Friday and I append a couple of ideas that came out of that. I should further note that I am not a member of the Modula 3 design or implementation team and do not speak for them, though of course I am trying to influence them! Here goes. Greg says: The point of <*USE*> is to prevent a reference from escaping into a global, not to prevent it from being modified. Mike says: The goal is to enable a compiler to prove that one value does not enable some subsidiary value to outlive the first value. Those are both subgoals. The real goal of USE is to allow the compiler to predict the life times of objects well enough so that it can allocate and free them more efficiently. Hmm, but that is also a subgoal. The REAL goal of USE is to let programmers feel free to use objects to represent any data type they want and not feel constrained by the cost of allocation and garbage collection. (Don't even consider for a moment that the real REAL goal is to nullify an important advantage C++ has over Modula 3.) The main ways the compiler can take advantage of life time predictions are: If an object can be shown to live no longer than the current stack frame it can be allocated on that stack frame. If two objects can be shown to have the same life time they can be allocated together, as a single GC unit on the heap or as a single unit on the stack. Greg says: I think it would be better to ... allow assignment to <*USE*> mode fields Mike says: There is no need to prevent assignments to stacked fields of REF RECORDs, The reason to prevent assignments to USE mode fields is because it would then allow the life time of the object referred to by the field to be shorter than the life time of the containing object. Assuming that the two objects are allocated together, we would not get timely collection of the object with the contained object. If timely collection of such objects is not important, then this restriction can be relaxed. However another reason to prohibit assignment to USE fields, and override to the field on NEW, is that actual the type of the object becomes fixed at compile time, this might allow one to inline method invocations. Greg says: I think it would be better to leave the rules for initializing fields alone Mike says: Without a prohibition on assignment, there's no need to generalize initialization. The reason for allowing NEW on initializers not directly related to the rule for not allowing assignment. It is instead to make it more likely that separate objects will have the same life time and thus allow them to be combined. Remember the example from my original message: INTERFACE Foo; TYPE T = OBJECT <*USE*> g1, g2: Goo.T; END; END Foo. and x := NEW(Foo.T, g1 := NEW(Goo.T), g2 := NEW(Goo.T)) because the NEWs are all done at one time and at one place the compiler can easily determine that the objects have the same life time and store them together. But when if Foo.T is opaque? E.g. INTERFACE Foo; TYPE T <: OBJECT METHODS init <*USE*> (); END; END Foo. Then the client of Foo cannot assign to g1 and g2 and it doesn't know from Goo. In the current language without NEW on initializers the client would allocate Foo.T and then call Foo.T.init to initialize the object, which could then call NEW and assign the result to the fields. That works fine, but the three objects have different life times and the compiler, without heroic inter-procedural analysis, could not determine that the objects could be stored together at the point it was allocating an instance of Foo.T. Note that the alternative of allocating a Foo.T, along with the Goo.T's, in a create function Foo.New is bad because it disallows the optimization of putting the Foo.T on the stack and it doesn't compose if Goo.T turns out to be an opaque object with fields. My proposal solves the problem IMPLEMENTATION Foo; REVEAL T = BRANDED OBJECT <*USE*> g1, g2: Goo.T := NEW(Goo.T); METHODS init <*USE*> (); END; END Foo. because at link time the implementation would know that every object allocated as a Foo.T always has two Goo.T's stored after it and it would know the length of a Goo.T and any types stored after it so that when allocating a Foo.T the total space needed could be allocated at one go and the cross links made. One could limit NEW to being used as an initializer only for USE fields but then USE would have to be promoted from pragma-hood to the language itself. Greg says: I think it would be exciting to change the field declaration buff: REF ARRAY OF CHAR in a stream, replacing it with <*USE*> buff: UNTRACED REF ARRAY OF CHAR after which DISPOSE(stream.buff) will be (or could be) safe! Only if stream was allocated in this procedure, consider PROCEDURE P(<*USE*> stream: Stream; VAR b: ARRAY OF CHAR) = BEGIN DISPOSE(stream.buff); END P; But do we really want programmers to get back into the job of explicit memory deallocation? Why not get rid of the UNTRACED and have the compiler generate the DISPOSE for you when it recognizes that the old value of stream.buff is dead? Also note that all this technique saves you is the cost of GC on stream.buff, not the cost of allocation or deallocation. I'll bet a lot of examples could be handled by INTEFACE Stream; TYPE Private <: ROOT; T <: Private OBJECT <*USE*> buff: REF ARRAY OF CHAR := NIL; (* Clients should allocate this but they should not look inside! *) METHODS init <*USE*> (); END; END Stream. A client could then say: WITH stream = NEW(Stream.T, buff := NEW(REF ARRAY OF CHAR, 5000)) DO ... END; which could allocate the Stream and the buffer on the stack. If one could get by with a fixed size buffer, then one can hide the buffer inside the stream. Mike says: Does the SRC compiler do enough dataflow analysis to enable checking the restrictions I'm suggesting <*STACKED*> could express? If not, I suspect one could get a decent approximation by local restrictions on the uses of a stacked component (in the style Garret suggests, but with more rules). If we are to get something like this accepted and implemented in every silly compiler, we have to be able to do it without real data flow analysis, that is, via simple local checks like the ones I proposed. The checks have to be expressed in terms, not in terms of complicated rules like whether a variable is dead, but by simple restrictions a programmer can remember and can deal with. Mike says: PROCEDURE Find(l: List.T; ...): REFANY = It's too easy to fake this for it to be a telling example for me. E.g. PROCEDURE Find(l: List.T; ...): REFANY = PROCEDURE HardCase(r: REFANY): REFANY = ...; PROCEDURE EasyCase(r: REFANY): REFANY = ...; TYPE Case = (Easy, Hard) VAR theCase: Case; PROCEDURE TheCase(r: REFANY): REFANY = BEGIN CASE theCase OF | Case.Easy: RETURN EasyCase(r); | Case.Hard: RETRUN HardCase(r); END; END TheCase; VAR ans: REFANY; BEGIN IF ... THEN theCase := Case.Hard ELSE theCase := Case.Easy END; WHILE l # NIL DO ans := TheCase(l.first); IF ans#NIL THEN RETURN ans END; l := l.rest; END; RETURN NIL; END Find; Mike says: PROC Client(...) = The problem is that Table.T.map has the wrong signature. It should accept a procedure, which would invariably be an internal procedure, rather than an object. (Though in fact one can think of a passed internal procedure as a funny kind of object. Think of the up level display as the private fields of the object.) I am working with people here to try and fix these problems, some of which were introduced when interfaces were translated from Modula 2+, which did not have objects and did not allow internal procedure to be passed as arguments, into Modula 3. Bill reminded me that a further use of USE is to mark a PROCEDURE argument. This ensures the client that it is okay to pass an internal procedure as an argument. I originally suggested to the M3 committee that internal procedures should have a different type than an external procedure with the same signature to get compile time checking of correct usage. This is similar but less fool proof. Bill brought up a few other very interesting points: - Is allocation, deallocation, and GC overhead a large part of the runtime of a Modula 3 application? My main experience with this has been with our Modula 2+ system where GC overhead sometimes consumed over 50% of the CPU cycles. Admittedly the collector was concurrent, designed for a multiprocessor and was optimized for small real time rather than small CPU time. The Modula 3 collector has very different characteristics but we both expected that a study would show that this is important. - Instead of trying to get rid of GC'ed objects (by combining them or putting them on the stack) wouldn't it be better to just make objects cheaper? Yes but I don't see any way to make them cheap enough to use really freely. That is one of the arguments people have had against languages that insist on everything being a pointer, e.g. ml or lisp. - How much will this technique save on existing objects? I can only guess, but I would guess not much. My guess is that the savings we get will come from allocating readers and writers on the stack and combining conditions with their containing objects. (Mutexes are often already combined by making them supertypes of the client type, which I think is a bit odd; I would rather see them as separate fields.) Remember this is a wild guess based on not too much information. I suspect the primary advantage will be that it will allow one to use object types in places where they cannot be used now because of efficiency considerations. - Does USE have any value to the programmer as part of a specification of an interface or is it just a compiler hint? More generally, what does a programmer wants to know about the use of the arguments: What operations might be called on this object while the call is outstanding? Update or just read operations? What operations might be called on this object after the call returns? Update or just read operations? USE says that there are no operations in the second category. This is a generally useful piece of information to the programmer. Note that for immutable objects this can be an over specification because one normally wouldn't care about read operations taking place after the call returns. - Couldn't USE end up being an over specification in some places? That is, couldn't it end up prohibiting some new implementations? As above, it can lead to over specification on immutable types. Consider the Text interface. In M3, Text's are implemented as REF ARRAY OF CHAR, while in Modula 2+ they are implemented as linked structures. Text.Cat in M3 just access its arguments in constructing a new text while in Modula 2+ the arguments are pointed to by the result. Marking the arguments of Text.Cat with USE would thus work for the current M3 implementation but would not allow a Modula 2+ style implementation. A sneaky way around this problem is to use <*USE*> on all immutable input parameters, but have implementations with USE define a generic interface: GENERIC INTERFACE GiveMe(Type); (* With Type.T <: REFANY. *) PROCEDURE P(<*USE*> r: Type.T): Type.T; (* Returns NIL if obj is not a top level object otherwise returns obj again. If a non NIL value is returned the result may be safely used in any assignment operation. *) END GiveMe. The implementation of GiveMe would be the identity on implementations without USE. On implementations with USE it will check the address to see if it points to the start of a heap object. An implementation of an operation on an immutable type that wants to keep a copy of its argument, like a Modula 2+ style Text.Cat, would then call the type's copy method. This method would take its argument as USE and return an object "equal" to its argument. This method could return GiveMe.P(self) if it is non NIL and allocate and initialize a new object otherwise. Garret Swart ======================================================================== 45 === Date: 25 Feb 92 18:31:01 GMT From: hudson@cs.umass.edu (Rick Hudson) Subject: Re: Safe cheap objects for Modula 3 swart@src.dec.com writes: > The main ways the compiler can take advantage of life time predictions > are: If an object can be shown to live no longer than the current > stack frame it can be allocated on that stack frame. Yes, and pending studies to the contrary, I would agree and such optimization might be very valuable. > If two objects > can be shown to have the same life time they can be allocated together, > as a single GC unit on the heap or as a single unit on the stack. In a copy collector, allocating two objects with one heap allocation might save the cost of incrementing the allocation pointer, but this isn't much of an advantage. If both objects die at the same time, whether they were allocated as one unit or not, makes no difference. The arguments that continued were well stated but assumed that there was an advantage to allocating single GC units. Later on you discuss the following: > - Instead of trying to get rid of GC'ed objects (by combining them > or putting them on the stack) wouldn't it be better to just make > objects cheaper? > > Yes but I don't see any way to make them cheap enough to use really > freely. That is one of the arguments people have had against > languages that insist on everything being a pointer, e.g. ml or > lisp. The folk lore that GC style object allocation is not cheap seems to continue, mostly being based on experience with non-generational or reference count style garbage collectors. Modula-3 differs greatly from Lisp in that it is statically typed. This allows a lot of the overhead of GC to be done away with. For example, look at the Gabriel benchmark "destruct" which was designed to test the speed of mutating objects in Lisp by assigning integers into slots in objects (actually cars in cons cells). If the Lisp has a generational GC it will spend a lot of time dealing with remembered set manipulations when the objects being stored are not pointers. Lisp has no way to tell this. To a great extent, remembered set processing is what the bench was designed to test. In Modula-3 we would know we were storing integers and 94% of the remembered set activity would be eliminated. Remembered set activity is also worth noting with regards to something said in an earlier letter. There was a discussion on how to best initialize objects. In a generational GC, time must be spent each time a pointer is stored to determine if the slot being stored into is in an older generation than the object the pointer refers to. If we know the object being stored into is the youngest object in the system we can eliminate this check. The current definition of NEW, when used with initialization values, allows such optimization. I currently do not know how valuable this optimization will be and would not like to see anyone change their current coding styles to take advantage of this optimization, but I just mention it as a case where changing coding style to facilitate one optimization on one compiler might defeat another optimization on another compiler. -- Richard L. Hudson, Research Associate University Computing Services Lederle Graduate Research Center University of Massachusetts Amherst, MA 01003 (413) 545-1220; Hudson@cs.umass.edu ======================================================================== 46 === Date: Tue, 25 Feb 1992 07:41:51 PST From: Mike_Spreitzer.PARC@xerox.com Subject: Re: Safe cheap objects for Modula 3 We actually agreed on the goal of predicting lifetimes of objects well enough to allocate and free them more efficiently (and I also introduced the goal of expressing the restriction on local procedures so that runtime checks need not be done). What I was trying to say is that that goal sets up a subgoal that has to be proven by induction over the structure of the data, and the more kinds of arcs in a type graph that can carry a nesting-of-lifetime restriction, the more cases where that proof will go through. The idea of inlining method invocation is attractive, but I suspect this trick will not get you very many cases. Most of the method inlining will require inter-procedural analysis. You don't need to allow NEW in initializers to allocate objects together. Consider your (non-opaque) example of Foo.T and Goo.T. Because your formulation of USE is so tight, the compiler could react to the declaration that the g1 and g2 fields of a Foo.T are USE by always allocating space for two Goo.T's with every Foo.T, and translating every "x.g1 := NEW(Goo.T)" (assuming you relaxed just enough to allow assignments to USE fields) into assigning the address of the first Goo.T space into x's g1 field. This space would go unused only when the programmer writes "x.g1 := Proc(...)" (which, as you've noted, is a weakness in any case). My understanding of memory managers is that the cost per object is much more significant than the cost per byte of object size, so this could be a good trade-off. Why does the current language require initialization to use only constant expressions? If we can change that enough to allow NEW in initialization, how much else is equally easy to allow? I presume you mean to prohibit assignment through REFs to whole RECORDs that contain (RECORDs that contain ...) USE fields. Calling USE an "over-specification" seems wrong. Using USE says something. Whether the author wants to say it is up to the author. If the author wants to say it, it is not an ober-specification. Put another way, the question is, "can USE be used unwisely?". The answer is "yes", and that's not a condemnation of USE. ======================================================================== 47 === Date: Tue, 25 Feb 92 10:14:04 PST From: Subject: Re: Safe cheap objects for Modula 3 What I was trying to say is that that goal sets up a subgoal that has to be proven by induction over the structure of the data, and the more kinds of arcs in a type graph that can carry a nesting-of-lifetime restriction, the more cases where that proof will go through. Good point. I was trying to be a bit more hard-ass and only look at arcs that carry "equivalent lifetime" restrictions. The idea of inlining method invocation is attractive, but I suspect this trick will not get you very many cases. Most of the method inlining will require inter-procedural analysis. One can inline a method invocation when one can statically statically determine the actual type of an object. There are two simple cases where this can be done, when an object is allocated as the expression of a WITH statement, E.g. WITH o = NEW(Foo.T) DO o.m1() END; or when an object is referred to by a field marked USE, using the rule that USE fields are only assigned to by the default value specifier. Because your formulation of USE is so tight, the compiler could react to the declaration that the g1 and g2 fields of a Foo.T are USE by always allocating space for two Goo.T's with every Foo.T Just knowing the type declaration of g1 and g2 only gives a lower bound on the size of the objects being referred to. One could always assign a Moo.T to a field of type Goo.T where Moo.T = Goo.T OBJECT cow: ARRAY[1..1000] OF CHAR; END; A Moo.T^ would certainly not fit in the room reserved for a Goo.T^. Your idea could still work in the case where one is careful to declare the actual type of the object rather than a super type. This mean that the extra space would go unused either if the programmer writes "x.g1 := Proc(...)" or when the programmer writes "x.g1 := NEW(Goo.T OBJECT ... END)". Why does the current language require initialization to use only constant expressions? If we can change that enough to allow NEW in initialization, how much else is equally easy to allow? It was my understanding that this discussion eventually ends up as part of the whole object initialization conundrum (what order should initialization calls be made in, why not implicitly call the init method after allocation) which Greg can explain much better than I. The current restrictions, and my extension, both have the property that a NEW never involves executing any user written code so that the interesting issues are avoided. I presume you mean to prohibit assignment through REFs to whole RECORDs tha t contain (RECORDs that contain ...) USE fields. Yes. Calling USE an "over-specification" seems wrong. You are right. The question I was trying to get at was whether USE is a useful part of a procedure specification. I guess my conclusion is that for mutable objects it is useful but for immutable objects it is not a useful because it says more than a programmer needs to say. The trouble is that the compiler is interested in USE and marking a procedures parameters with USE allows the procedure to be used more efficiently. Thus it is tempting to use it inappropriately. I also introduced the goal of expressing the restriction on local procedures so that runtime checks need not be done I've come to the conclusion that procedures passed as arguments should always be passed in USE mode, thus always allowing local procedures to be used as arguments. If an interface accepts a procedure in any other mode the interface designer screwed up: it should be accepting a closure object instead. USE mode procedures should be used in cases where you promise to be done with the procedure before the call returns, e.g. Table.T.map. Closures should be used in cases where that promise cannot be made, e.g. Thread.Fork. Thus if good programming practice is being used there should never be a runtime check that a procedure parameter is assignable. I think I can say something still stronger: When good programming practice is being used the only variables of type PROCEDURE should be parameters. (You mean Pascal got it right? Not really, because Pascal doesn't have objects.) [Note that there are examples where passing a procedure and a REFANY is more efficient than using an object because it avoids an extra allocation. E.g. instead of TYPE MyClosure = Thread.Closure OBJECT text: TEXT OVERRIDES apply = MyApply END; Thread.Fork(NEW(MyClosure, text := t)) one could avoid the NEW by passing the TEXT as a REFANY Thread.Fork(MyApply, t) But in most cases the designer of the interface can get the allocation back by giving the closure an opaque supertype and using that for the implementation's own data. For example, Thread.Closure could be implemented as subtype of Thread.T.] Garret ======================================================================== 48 === Date: Tue, 25 Feb 92 09:58:37 PST From: mjordan@src.dec.com (Mick Jordan) Subject: Re: Safe cheap objects for Modula 3 Modula-3 provides a lot of support for abstraction that, if used extensively, makes the generation of high quality code for separately compiled modules essentially impossible. E.g. "virtual" method calls that arent, opaque supertypes, NARROW calls to leaf types, inline procedures, NEW calls that could be allocated on the stack and so on. Trying to fix these problems with pragmas is, I believe, a mistake. Instead, we should focus on generating optimal code for complete programs, where the abstraction scaffolding can be torn down. Recently, I posted a message alluding to a tool that would perform procedure inlining. In practice this tool is the beginning of a whole program optimiser for Modula-3, which is focussed on the kinds of transformations that the USE pragma is aimed at. Currently the tool is capable of the following optimisations: 0. Removing the addresssing overhead of opaque supertypes, which comes for free by compiling with knowledge of the concrete revelations. 1. Removing unreachable procedures, including those that are merely specified as method overrides if they can never be invoked. 2. Translating method calls into procedure calls if only one procedure value is possible. T.m is a trivial case of this. 3. Translating TYPECASE into CASE, when all the type labels are disjoint. (This can be the basis of method inlining, when more than one procedure value is possible.) In its present form the tool does not do dataflow analysis; it is suprising what can be achieved with only simple analysis. It works on big programs, provided you have enough VM, e.g. BadBricks and, in its current form, generates Modula-3 as output (which is something of a limitation but has some virtues). The tool is based on the Modula-3 toolkit ASTs, and I plan to ship it with the 2.0 toolkit later this year. Mostly due to optimisation 3, I get a factor of two speedup in the toolkit compiler itself, which I find pretty exciting. Mick Jordan ======================================================================== 49 === Date: Tue, 25 Feb 92 14:40:25 EST From: wyant@centerline.com(Geoff Wyant) Subject: Re: Safe cheap objects for Modula 3 I agree with Rick Hudson. It seems like a lot of thought is being expended on how to work around the garbage collector. I don't believe that garbage collection HAS to be that expensive that pragmas need to be invented to avoid it. Modern generational collectors can make allocation no more expensive than incrementing a pointer. Deallocation can be somewhat more expensive, but with a tuneable collector such as the one that GNU M3 project is working on, the costs should be quite controllable. Geoff Wyant wyant@centerline.com Centerline Software, Inc. (Formerly Saber Software, Inc) 10 Fawcett Street Cambridge, Ma. ======================================================================== 50 === Date: Tue, 25 Feb 1992 21:25:55 GMT From: tia@sunb2.cs.uiuc.edu (Too-Seng Tia) Subject: questions on modula-3 I am trying to learn Modula-3 (by reading some sample programs :( ). I have a couple of questions : (1) Are there any good textbooks on Modula-3? (2) We have version 1.6 of SRC Modula-3 compiler. Is there a language manual for the compiler? (3) I tried to compile the following module taken from an article in the BYTE magazine (Nov 90), but can never get it to compile. Can someone tell me whether I have made a mistake somewhere? The compiler tells me that I have a syntax error (extra tokens) in the line with "MODULE Point;" ================================================== INTERFACE Point; TYPE T<:Public_T; Public_T=OBJECT METHODS PosX( ):REAL; PosY( ):REAL; END; <*INLINE*> PROCEDURE New(x,y:REAL):T; END Point. MODULE Point; REVEAL T=Public_T BRANDED OBJECT X,Y:REAL; METHODS PosX := PosXProc; PosY := PosYProc; END; PROCEDURE New(x, y:REAL):T= BEGIN RETURN NEW(T,X:=x,Y:=y); END New; PROCEDURE PosXProc(p:T):REAL= BEGIN RETURN p.X; END PosXProc; PROCEDURE PosYProc(p:T):REAL= BEGIN RETURN p.Y; END PosYProc; BEGIN END Point. ==================================================== (4) I was trying to implement a tree module. Here are my interface and part of the module : INTERFACE Tree; IMPORT Info, Text; TYPE T<:Public; Public=OBJECT METHODS make (): T; empty (): BOOLEAN; insert (node: T; key: TEXT); setInfo (info: Info.T); setLeft (left : T); setRight(right: T); info (): Info.T; left (): T; right (): T; print (); END; END Tree. MODULE Tree; IMPORT Info, Text, Wr, Stdio; REVEAL T=Public BRANDED "Tree.T" OBJECT infoPtr: Info.T; leftPtr, rightPtr: T; OVERRIDES make := make; empty := empty; insert := insert; setInfo := setInfo; setLeft := setLeft; setRight := setRight; info := info; left := left; right := right; print := print; END; ============================== If I replaced the keyword "OVERRIDES" with "METHODS", I get some compilation errors. If I omit "OVERRIDES" and all the method := method that follows it altogether, I get some "illegal instruction" error during runtime. My question is - why do I need the "OVERRIDES"? I suppose in object-oriented programming, there are many occasions where we want to override some methods in the superclass. But I don't understand why I need it here. Any help is greatly appreciated. Thanks. ======================================================================== 51 === Date: Tue, 25 Feb 92 16:06:20 PST From: muller@src.dec.com (Eric Muller) Subject: Re: questions on modula-3 In article <1992Feb25.212555.15707@sunb10.cs.uiuc.edu>, tia@sunb2.cs.uiuc.edu ( Too-Seng Tia) writes: > (1) Are there any good textbooks on Modula-3? There are two books on Modula-3: @book (m3-Nel91, title = "System Programming with Modula-3", editor = "Greg Nelson", publisher = "Prentice Hall", isbn = "0-13-590464-1", year = 1991, what = "The bible for Modula-3. Includes the language reference manual and papers on the I/O library, threads, and the Trestle window system." ) @book (m3-Har92, title = "Modula-3", author = "Samuel P. Harbison", publisher = "Prentice Hall", isbn = "0-13-596396-6", year = 1992, what = "A complete Modula-3 textbook covering the full language, with examples and exercises. Includes a style manual and a user's guide for SRC Modula-3." ) The first is a reference and deals with some advanced topics. The second should now be available in all bookstores and is a textbook (I got one yesterday at the downtown branch of the Stanford Bookstore). > (2) We have version 1.6 of SRC Modula-3 compiler. Is there a language manual > for the compiler? Version 1.6 is now obsolete. In addition to a number of bugs that have been fixed, it implements an earlier version of the language than described in the two books above. You should really get 2.03 from gatekeeper.dec.com, in pub/DEC/Modula-3. If you insist on using 1.6, the language implemented by that version is described in: @techreport (m3-CDG*89, author = "Luca Cardelli and James Donahue and Lucille Glassman and Mick Jordan and Bill Kalsow and Greg Nelson", title = "Modula-3 Report (revised)", number = "52", type = "SRC report", institution = "System Research Center, Digital Equipment Corporation", address = "Palo Alto", month = nov, year = 1989, howtoget = "Via anonymous ftp on gatekeeper.dec.com, in pub/DEC/Modula-3/Report.ps. This file has been formatted for an Apple Laserwriter. The files Report{1,2,3}.ps are also the report, in three postscript files, to accomodate small printers. On paper, by sending a mail to src-report@src.dec.com.", what = "This report is {\em the} definition of the language." ) In addition, SRC Modula-3 has some release notes. You should be able to get them from person who installed SRC Modula-3 at your site. If not, the 1.6 release notes can still be found in gatekeeper.dec.com:pub/DEC/Modula-3/m3-1.6/dist-1.6.tar.Z. The 2.0 release notes are in gatekeeper.dec.com:pub/DEC/Modula-3/m3-2.0/doc-2.03.tar.Z. > (3) I tried to compile the following module taken from an article in the > BYTE magazine (Nov 90), but can never get it to compile. Can someone > tell me whether I have made a mistake somewhere? The compiler tells me > that I have a syntax error (extra tokens) in the line with "MODULE Point; " Could it be that you have everything in one file ? SRC Modula-3 requires that each compilation unit be in a separate file; the interface I must be in the file I.i3. There is also the convention that the module M be in the file M.m3. > (4) I was trying to implement a tree module. Here are my interface and > part of the module : > > ... > > If I replaced the keyword "OVERRIDES" with "METHODS", > I get some compilation errors. If I omit "OVERRIDES" and all the > method := method that follows it altogether, I get some "illegal instruction" > error during runtime. My question is - why do I need the "OVERRIDES"? > I suppose in object-oriented programming, there are many occasions where > we want to override some methods in the superclass. But I don't understand > why I need it here. What are the compilation errors ? Could it be that the procedures "make" and so on do not have the proper declaration ? They should be like: PROCEDURE make (self: T): T = ... (where any identified instead of self is ok). If you just remove OVERRIDES and what follows it, instances of the type do not have procedures associated with the methods (i.e. the procedure associated with the method is NIL) and you get a checked runtime error (i.e. the program somehow crashes). This also explains why you need an OVERRIDES: to associate some procedures to the methods. -- Eric. ======================================================================== 52 === Date: Wed, 26 Feb 1992 11:20:51 PST From: Hans_Boehm.PARC@xerox.com Subject: Re: Safe cheap objects for Modula 3 Let's keep things in perspective. There is a significant cost to allocating collectible storage rather than stack allocating storage. At a minimum, the first read access to every newly allocated cache line in the heap is likely to miss (unless writes force cache reloads, which has other costs). Furthermore heap allocation with a direct mapped cache is likely to trash everything else in the cache periodically. You may need some locking or synchronization on allocation, or risk fragmenting the heap by dividing it up on a per thread basis. On the other hand, stack allocating memory that is known not to outlive a procedure activation is also dubious in general. David Chase had a paper a while back describing some of the things that can go wrong. The most obvious one is that the procedure may allocate a gigabyte of data, most of which dies a long time before the procedure exits. Nonetheless, I think it's profitable to do so, when you can determine it's safe. If your collector supports optional explicit deallocation, then that can also alleviate the cache problems, and is probably less dangerous, but more expensive in other respects. Hans ======================================================================== 53 === Date: Thu, 27 Feb 92 13:47:47 -0500 From: Norman Ramsey Subject: SRC M3 implementation of exceptions Apologies if this article has already appeared. I fear our news posting software is wonky again. A while back, a newcomer asked about the SRC M3 implementation of exceptions using setjmp and longjmp. As noted in the installation guide, assignments to variables may have been undone when the exception-handling code executes. I've actually been bitten by this problem a couple of times, most frequently in lexical analysis, which has to perform nontrival computations when handling Rd.EndOfFile. I need to compile the code with optimization because it's a performance bottleneck. What I do to fix the exception problem is to take the address of every scalar variable that's used in an exception handler. The address-taking code doesn't actually have to be executed very often (or even at all); as long as the compiler thinks the address of a variable might be taken, it won't allocate the variable to a register. So, would the M3 compiler people consider doing automating this process? Adding the following code to an exception handler should do it: pointless_global_variable = (unsigned) &n + (unsigned) &m + ... ; I think the cost of executing this code in every exception handler should be acceptable; it should be lost in the noise of the setjmp/longjmp overhead. Norman Ramsey nr@princeton.edu ======================================================================== 54 === Date: Thu, 27 Feb 92 15:58:27 +0100 From: Piet van Oostrum Subject: Article "Modula-3 type systems" I am trying to locate the article "The Modula-3 type System". I think by Cardelli. I have had it but can't find it back. It would be nice if anybody had a machine-readable version, even better if it had been updated with respect to the language changes. I want to give it as a handout to my students on the "Object oriented programming" course. Piet* van Oostrum, Dept of Computer Science, Utrecht University, Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands. Telephone: +31 30 531806 Uucp: uunet!mcsun!ruuinf!piet Telefax: +31 30 513791 Internet: piet@cs.ruu.nl (*`Pete') ======================================================================== 55 === Date: Thu, 27 Feb 92 06:57:51 PST From: bates@awsil4.boeing.com Subject: X11R4 Ultrix compatibility Does anyone know what version of X11 comes with Ultrix 4.1 and 4.2? (This if for the DEC Station 3100.) The m3-1.6 X11R4 demos won't link to the X11 libraries I got with Ultrix. Both the Ultrix release notes and DEC support claim that this is fully compatible with X11R4. But the links fail with undefined symbols, the most common of which is XtAppInitialize. The C header files which came with Ultrix are also missing XtAppInitialize and a couple of others at the place where the m3 interface has them. Other nearby procedures match between the C header files and the m3 interfaces. Rod Bates ======================================================================== 56 === Date: 27 Feb 92 14:38:47 GMT From: moss@cs.umass.edu (Eliot Moss) Subject: Re: Safe cheap objects for Modula 3 >>>>> On 26 Feb 92 19:20:51 GMT, Hans_Boehm.PARC@xerox.com said: Hans> Let's keep things in perspective. There is a significant cost to Hans> allocating collectible storage rather than stack allocating storage. Hans> [...] You may need some locking or synchronization on allocation, or Hans> risk fragmenting the heap by dividing it up on a per thread basis. What I had in mind for gm3 was a generational heap where each thread has its own nursery but the older spaces are shared. One then needs synchronization only on scavenging and promotion, which require coordination anyway. I'd be interested in folks thoughts on this approach (not on generational vs. other schemes, but this multi-thread generational scheme). I'd like to explore the optimization of stack allocation of some objects, and in fact have a pending grant proposal on gc that would include that as part of a comprehensive gc system. I agree that stack allocation is cheaper than heap allocation, but I want to get some numbers that show the difference. Maybe gm3 will be running well enough soon that we can try some tests to see the difference in cost with our collector. -- J. Eliot B. Moss, Assistant Professor Department of Computer Science Lederle Graduate Research Center University of Massachusetts Amherst, MA 01003 (413) 545-4206, 545-1249 (fax); Moss@cs.umass.edu ======================================================================== 57 === Date: Thu, 27 Feb 1992 14:12:13 PST From: Hans_Boehm.PARC@xerox.com Subject: Re: SRC M3 implementation of exceptions The rationale of the ANSI C standard states "In fact, the only reliable way to ensure that a local variable retain (sic) the value it had at the time of the call to longjmp is to define it with the volatile attribute." Unfortunately, the bundled Sun C compiler doesn't understand volatile. But both gcc and the unbundled compiler do, I believe. If all else fails, you could presumably insert a # define volatile and be back to where you started. I think the real cost of either this proposal is that forcing variables into memory is expensive, so it would have to be done judiciously. Hans ======================================================================== 58 === Date: Fri, 28 Feb 1992 11:09:53 PST From: Hans_Boehm.PARC@xerox.com Subject: Re: SRC M3 implementation of exceptions That's what I meant. I was quoting from section 4.6.2.1 of the ANSI C standard and rationale. (I just finished a net discussion about longjmp, malloc, and signals. That's why I happen to know about that section. I agree that this use of volatile doesn't seem to have much to do with its main purpose.) Hans ======================================================================== 59 === Date: Fri, 28 Feb 92 13:49:47 -0500 From: Norman Ramsey Subject: Re: SRC M3 implementation of exceptions Hans_Boehm.PARC@xerox.com writes... > The rationale of the ANSI C standard states > > "In fact, the only reliable way to ensure that a local variable retain (sic) > the value it had at the time of the call to longjmp is to define it with the > volatile attribute." drh@princeton.edu (Dave Hanson) writes... > there's no such stipulation in the standard that > i can find. basically, all volatile does is prohibit > caching, e.g., > > volatile int x; > x = 2; > y = x; > > 2 must be stored in x and fetched again. the compiler > is not allowed to avoid the fetch or delay the store. Hans_Boehm.PARC@xerox.com writes... > I think the real cost of either this proposal is that forcing variables into > memory is expensive, so it would have to be done judiciously. It is expensive, but putting the variables in registers violates the semantics of Modula-3, which isn't any good. In the construct TRY mumble EXCEPT foo END; then x can be allocated to a register unless mumble sets x and foo reads x. Is this what you had in mind when you said `judiciously'? Norman Ramsey nr@princeton.edu ======================================================================== 60 === Date: 28 Feb 92 23:15:42 GMT From: chased@rbbb.Eng.Sun.COM (David Chase) Subject: Re: SRC M3 implementation of exceptions A couple of clarifications on what volatile does, must do, and does not do: 1) I believe the standard allows a volatile register variable, though I know of no such implementation. 2) Sun's C compilers, certainly, and I believe others as well, get sufficiently paranoid when they see "setjmp" that no volatile declarations should be necessary. This is also a consequence of the parameter passing standard for floating point numbers, the standard calling sequence, and implementation of setjmp and longjmp. (We suffer a fair amount of casual bashing for our calling conventions -- we ought to get some benefit out of them. NB "setjmp.h" must be included, else the magic words are not uttered and setjmp is just another function.) 3) The ANSI specification for volatile is pretty useless. It doesn't require any multiprocessor synchronization, since C (in the standard) is a single-threaded language, and (in particular) "sequence points" are defined with respect to a single thread of execution. Also, a careful reading of the standard will indicate that volatile accesses can be reordered with respect to other accesses, as long as those other accesses are not also volatile (page 9, ANSI C standard, beginning "The least requirements on a conforming implementation are"). Most likely, the barrier instructions will end up embedded within POSIX compliant thread libraries. Last I checked, they said all the right words about unguarded concurrent access to shared variables. If your C compiler emits memory barrier instructions, that's ok, but mighty conservative. David Chase Sun ======================================================================== 61 === Date: Fri, 28 Feb 92 15:02:51 EST From: Dave Hanson Subject: SRC M3 implementation of exceptions Date: Fri, 28 Feb 92 13:49:47 -0500 From: Norman Ramsey Hans_Boehm.PARC@xerox.com writes... > The rationale of the ANSI C standard states > > "In fact, the only reliable way to ensure that a local variable retain (si c) > the value it had at the time of the call to longjmp is to define it with t he > volatile attribute." drh@princeton.edu (Dave Hanson) writes... > there's no such stipulation in the standard that > i can find. basically, all volatile does is prohibit > caching, e.g., > > volatile int x; > x = 2; > y = x; > > 2 must be stored in x and fetched again. the compiler > is not allowed to avoid the fetch or delay the store. insufficient quote! what I said was that there's no stipulation in the ANSI standard against using registers to volatile variables. it's OK for an implementation can put volatile variables in registers if it retains its value at the time of a longjmp. I think Hans's interpretation of sec. 4.6.2.1 is correct... ======================================================================== 62 === Date: Fri, 28 Feb 1992 20:39:29 GMT From: prj@gamba.lcs.mit.edu (Paul R. Johnson) Subject: Re: SRC M3 implementation of exceptions Sorry, I spoke too soon and was thinking CLU exception semantics rather than Modula-3. In CLU the construct begin mumble end except when Exception: foo end bar would, if the signal Exception were raised in mumble, execute foo and then (unless foo returned or signaled) continue on with bar. Personally I prefer this to Modula-3's exception semantics as it makes exceptions signaled by a procedure simply an alternatve form of return. This is often useful to handle expected (i.e., not badly exceptional) but non-normal cases such as "end-of-file" or "not-found". ---Paul Johnson ======================================================================== 63 === Date: Fri, 28 Feb 92 12:00:58 PST From: Subject: Re: SRC M3 implementation of exceptions A cost of using volatile rather than the implementation dependent hack that Norm suggests is that accessing a volatile variable acts as a memory barrier for all variables. That means that on a multiprocessor special instructions have to be issued in order to flush write behind and invalidate noncoherent read caches. Garret ======================================================================== 64 === Date: Fri, 28 Feb 1992 12:51:04 PST From: Hans_Boehm.PARC@xerox.com Subject: Re: SRC M3 implementation of exceptions "A cost of using volatile rather than the implementation dependent hack that Norm suggests is that accessing a volatile variable acts as a memory barrier for all variables. That means that on a multiprocessor special instructions have to be issued in order to flush write behind and invalidate noncoherent read caches." This is clearly true for volatile "extern" or "static" variables. But those aren't the problem. I agree with Dave Hanson that there doesn't appear to be any rule against putting local volatile variables in registers. Similarly, I'm not sure that there are any restrictions (other than the desirable one on longjmp behavior) on what the compiler is allowed to do with volatile stack allocated variables whose address is never taken. Such variables presumably can't be seen by a signal handler or another process. I'm not terribly confident of this interpretation, though. On the other hand, my impression is that you are correct as far as the behavior of current compilers is concerned. Volatile does seem to force similar treatments for local and nonlocal variables, even on SPARCs, where longjmp doesn't restore old register values. Hans ======================================================================== 65 === Date: 28 Feb 92 20:27:18 GMT From: prj@gamba.lcs.mit.edu (Paul R. Johnson) Subject: Re: SRC M3 implementation of exceptions nr@princeton.edu (Norman Ramsey) writes... >It is expensive, but putting the variables in registers violates the >semantics of Modula-3, which isn't any good. In the construct > > TRY > mumble > EXCEPT > foo > END; > >then x can be allocated to a register unless mumble sets x and foo >reads x. Is this what you had in mind when you said `judiciously'? In fact, I believe, in the construct TRY mumble EXCEPT foo END; bar then x can be allocated to a register unless mumble sets x and foo OR bar reads x. ---Paul Johnson ======================================================================== 66 === Date: 29 Feb 92 04:36:44 GMT From: moss@cs.umass.edu (Eliot Moss) Subject: Re: SRC M3 implementation of exceptions By the way, pursuant to the discussion of the correct implementation of exception w.r.t. variables allocated to registers, gm3 "does the right thing". It will restore the register contents for a variable globally (i.e., across multiple basic blocks) allocated to a register. It does this by (a) allowing such variables to be allocated only to callee saved registers and (b) going through each procedure's return sequence so that callee saved registers are restored. (This works in the raising procedure because a RAISE is a call to a run-time routine.) The unwinding happens by changing return addresses; the correct return address is found using a compiler generated table. This is all operatoinal now. The one thing we do not support is restoration of caller saved registers (gcc's scheme for that is a pain for our use and would require reworking or else would result in rather large tables, etc.). Just thought folks would like to know that we think about such things .... Eliot -- J. Eliot B. Moss, Assistant Professor Department of Computer Science Lederle Graduate Research Center University of Massachusetts Amherst, MA 01003 (413) 545-4206, 545-1249 (fax); Moss@cs.umass.edu