From jayk123 at hotmail.com Sun May 2 21:31:07 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 2 May 2021 19:31:07 +0000 Subject: [M3devel] Tipe vs. Type Message-ID: m3front has something called Tipe and something called Type. This seems kinda bad/confusing. m3front is light on comments. Of course there is source and I could/should step through it. Anyone know what these represent? Thank you, - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Sun May 2 23:40:39 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Sun, 2 May 2021 16:40:39 -0500 Subject: [M3devel] Tipe vs. Type In-Reply-To: References: Message-ID: <804c6d9a-deac-f81e-c0c6-35cceb338b73@lcwb.coop> I've done lots of work involving both. It's a mess. If there's any rhyme or reason to when which spelling is used, I have been unable to discern it. Some/many object types have fields of both names, usually of the same type Type.T, often redundantly having the same value, but don't rely on that. Often one is in a supertype, the other in a subtype. Different subtypes of the same supertype will repeat the second spelling, often with the same value as each other. It takes some careful vetting of each object subtype to be sure. I have left comments in a place or two to the effect that the type and tipe fields are fully redundant and should be combined. It looks like just laziness, not checking what is inherited and choosing a cutesy alternate spelling whose only value is its different spelling, but again, don't rely on it. Did I mention that it's not especially consistent? Good luck. HTH. . On 5/2/21 2:31 PM, Jay K wrote: > m3front has something called Tipe and something called Type. > This seems kinda bad/confusing. m3front is light on comments. > Of course there is source and I could/should step through it. > > Anyone know what these represent? > > Thank you, > ?- Jay > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From vvm at tut.by Tue May 4 15:02:06 2021 From: vvm at tut.by (vvm at tut.by) Date: Tue, 04 May 2021 16:02:06 +0300 Subject: [M3devel] SWIG Issue #1447 Fw: Re: m3swig Re: LLVM 9.0.1, bindings and Hello.exe for AMD64_NT Re: swigm3 Fw: swig-4.0.2-modula3-supported In-Reply-To: <3594361619417269@mail.yandex.by> References: , <3594361619417269@mail.yandex.by> Message-ID: <1399941620132395@mail.yandex.by> An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Tue May 4 17:53:12 2021 From: rodney_bates at lcwb.coop (Rodney Bates) Date: Tue, 4 May 2021 08:53:12 -0700 Subject: [M3devel] Tipe vs. Type Message-ID: <20210504085312.FAB8D415@m0117565.ppops.net> Ah, now I understand the question. I was thinking about fields named 'type' and 'tipe'. I forgot about modules named 'Type' and 'Tipe' There are two different sets of descriptions of reference types that go right in the object constant area, not debug info. They have somewhat different contents. One is used by the runtime for things like GC, TYPECASE, etc. The other (Tipe, I think) is used by Pickles. I may not have the details exactly right, but the distinction is at least pretty consistent, other than the names' giving no hint to what the difference is. -Rodney Bates --- rodney_bates at lcwb.coop wrote: From: "Rodney M. Bates" To: Jay K , m3devel , Rodney Bates Subject: Re: [M3devel] Tipe vs. Type Date: Sun, 2 May 2021 16:40:39 -0500 I've done lots of work involving both. It's a mess. If there's any rhyme or reason to when which spelling is used, I have been unable to discern it. Some/many object types have fields of both names, usually of the same type Type.T, often redundantly having the same value, but don't rely on that. Often one is in a supertype, the other in a subtype. Different subtypes of the same supertype will repeat the second spelling, often with the same value as each other. It takes some careful vetting of each object subtype to be sure. I have left comments in a place or two to the effect that the type and tipe fields are fully redundant and should be combined. It looks like just laziness, not checking what is inherited and choosing a cutesy alternate spelling whose only value is its different spelling, but again, don't rely on it. Did I mention that it's not especially consistent? Good luck. HTH. . On 5/2/21 2:31 PM, Jay K wrote: > m3front has something called Tipe and something called Type. > This seems kinda bad/confusing. m3front is light on comments. > Of course there is source and I could/should step through it. > > Anyone know what these represent? > > Thank you, > ?- Jay > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel From rodney_bates at lcwb.coop Tue May 4 18:06:28 2021 From: rodney_bates at lcwb.coop (Rodney Bates) Date: Tue, 4 May 2021 09:06:28 -0700 Subject: [M3devel] declare_param typenames Message-ID: <20210504090628.FAB8D5DC@m0117565.ppops.net> -Rodney Bates --- jayk123 at hotmail.com wrote: From: Jay K To: m3devel Subject: [M3devel] declare_param typenames Date: Tue, 27 Apr 2021 17:01:44 +0000 Fyi declare_param lacks typenames. It only has typeid. So it is a bit lossy. C backend could use better fidelity here. I intend to add a M3.QID parameter. The reader/writer can ignore it, so m3cc completely unchanged. M3x86 and M3Llvm can also ignore it, requiring a few lines. - Jay I am in favor this and similar changes. While our compilation system is quite good at handling separate unit compilation and recompilation within a package, the inter-package stuff emits nearly unusable error messages for inconsistencies, missing types, etc. All they give is a UID for problem types, and not even in the same base as elsewhere. Diagnosing such problems is a mix of sheer guesswork and dumb luck. Mercifully, they don't happen real often, at least not to me. I have toyed with improving the messages in the past, but it first entails putting type names in a number of the IR operators. Each such change has to be made in somewhere around 10 different places, so it hasn't made it to the top of my priority list. _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel From jayk123 at hotmail.com Tue May 4 23:36:49 2021 From: jayk123 at hotmail.com (Jay K) Date: Tue, 4 May 2021 21:36:49 +0000 Subject: [M3devel] declare_param typenames In-Reply-To: <20210504090628.FAB8D5DC@m0117565.ppops.net> References: <20210504090628.FAB8D5DC@m0117565.ppops.net> Message-ID: I am not sure if what I want to do will help that. Whenever I get those errors I just rebuild from m3core and up. I appreciate that the checking exists, at least. It was probably the best system by far at the time it was written, and still competitive. I do see similar errors from C++ LTCG/LTO about multiple sizes for same type, and the type names are given, and C++ users and providers do want to provide ODR violation errors, but it is a very long time coming. I learned to not hold my breath. - Jay ________________________________ From: Rodney Bates Sent: Tuesday, May 4, 2021 4:06 PM To: Jay K ; rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames -Rodney Bates --- jayk123 at hotmail.com wrote: From: Jay K To: m3devel Subject: [M3devel] declare_param typenames Date: Tue, 27 Apr 2021 17:01:44 +0000 Fyi declare_param lacks typenames. It only has typeid. So it is a bit lossy. C backend could use better fidelity here. I intend to add a M3.QID parameter. The reader/writer can ignore it, so m3cc completely unchanged. M3x86 and M3Llvm can also ignore it, requiring a few lines. - Jay I am in favor this and similar changes. While our compilation system is quite good at handling separate unit compilation and recompilation within a package, the inter-package stuff emits nearly unusable error messages for inconsistencies, missing types, etc. All they give is a UID for problem types, and not even in the same base as elsewhere. Diagnosing such problems is a mix of sheer guesswork and dumb luck. Mercifully, they don't happen real often, at least not to me. I have toyed with improving the messages in the past, but it first entails putting type names in a number of the IR operators. Each such change has to be made in somewhere around 10 different places, so it hasn't made it to the top of my priority list. _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cd05c0073508746786ff008d90f169832%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637557411976234635%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=%2Fm7ET0tgq6rwrr1Kr7j32l5c7NpBD7FUP95JHXNyzz4%3D&reserved=0 -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Tue May 4 23:51:26 2021 From: jayk123 at hotmail.com (Jay K) Date: Tue, 4 May 2021 21:51:26 +0000 Subject: [M3devel] force push to master? In-Reply-To: References: Message-ID: 1. I want to acknowledge, that I have, a small number of times, force pushed to master. Always a "short" time after whatever commit I want to replace, for some definition of "short". Always double checking I am replacing master/head, which I understand is a race condition. 1. Very broadly speaking, I know this is frowned upon. 2. In small projects, maybe ok?? 3. Maybe we should define "short"? 4. Or just ban it? I also read history a bit obsessively, and anyone that does similar will notice if I ever lose the race condition and replace a commit. Which I realize you are not really expected to have to do. Commits should be sticky, unless reverted, and that is still sticky. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Wed May 5 06:39:54 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 5 May 2021 04:39:54 +0000 Subject: [M3devel] Fw: [M3commit] [modula3/cm3] 37cccc: m3front: Move NamedType.qid to Type.Info. In-Reply-To: References: Message-ID: I ended up force pushing to undo this, having eventually found a solution without it. Though I don't think was too terrible. - Jay ________________________________ From: M3commit on behalf of Jay Krell Sent: Tuesday, May 4, 2021 8:54 AM To: rodney.m.bates at acm.org ; m3commit at elegosoft.com Subject: [M3commit] [modula3/cm3] 37cccc: m3front: Move NamedType.qid to Type.Info. Branch: refs/heads/master Home: https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmodula3%2Fcm3&data=04%7C01%7C%7Ca01de2a00bee44ae5dd208d90eda581c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637557153220649434%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=WoF9Igrb1bEcl3oagLuU8TSPFn%2BjAlKPkIbTKLW6MCk%3D&reserved=0 Commit: 37cccce0f8cf75e5aaccbe57d12e51373743e375 https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmodula3%2Fcm3%2Fcommit%2F37cccce0f8cf75e5aaccbe57d12e51373743e375&data=04%7C01%7C%7Ca01de2a00bee44ae5dd208d90eda581c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637557153220649434%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=epGj%2B6g%2BayIfEMCVahWTM6CYWQQST2hxX%2BXHah7HIr8%3D&reserved=0 Author: JayKrell Date: 2021-05-04 (Tue, 04 May 2021) Changed paths: M m3-sys/m3front/src/types/NamedType.m3 M m3-sys/m3front/src/types/Type.i3 Log Message: ----------- m3front: Move NamedType.qid to Type.Info. This does bloat non-named types by two integers but this is likely the most elegant solution. _______________________________________________ M3commit mailing list M3commit at elegosoft.com https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3commit&data=04%7C01%7C%7Ca01de2a00bee44ae5dd208d90eda581c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637557153220649434%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=N11npuWOo6bCcADN2ziPFnAtydRAJw0tRXEpFhUslP8%3D&reserved=0 -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Wed May 5 06:45:27 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 5 May 2021 04:45:27 +0000 Subject: [M3devel] typename foo.bar vs. bar vs. foo__bar Message-ID: m3c/m3front: Change typenames from: ? modula3/cm3 at c847fcd (github.com) m3c/m3front: Change typenames from: e.g. m3front passes:const_void_star, m3c produces:m3core_Uuio_const_void_star to m3front passes:Ctypes__const_void_star and m3c leaves it alone. Since types have no linkage, there is danger of collision. We'll see. Perhaps this was too cavalier. I have never run the LLVM stuff and m3gdb rarely, sorry. If this breaks either of them, I can make it only diverge if backend mode == C. Or, we could pass the qid to the backends and they could combine the pieces as they see fit. Or we could pass two specific combinations and backend would chose among them. (perhaps two combinations and uncombined qid, so all existing backends would chose one precombined, and hypothetical other backends could combine itself). This is "only" debug-info related, and typenames are kinda a weak but somewhat preexisting area there. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Wed May 5 06:46:47 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 5 May 2021 04:46:47 +0000 Subject: [M3devel] typename foo.bar vs. bar vs. foo__bar In-Reply-To: References: Message-ID: More generally, if it works, I'd just as soon have it always be foo__bar and remove the parameters. For all calls to Value.GlobalName. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 5, 2021 4:45 AM To: m3devel Subject: [M3devel] typename foo.bar vs. bar vs. foo__bar m3c/m3front: Change typenames from: ? modula3/cm3 at c847fcd (github.com) m3c/m3front: Change typenames from: e.g. m3front passes:const_void_star, m3c produces:m3core_Uuio_const_void_star to m3front passes:Ctypes__const_void_star and m3c leaves it alone. Since types have no linkage, there is danger of collision. We'll see. Perhaps this was too cavalier. I have never run the LLVM stuff and m3gdb rarely, sorry. If this breaks either of them, I can make it only diverge if backend mode == C. Or, we could pass the qid to the backends and they could combine the pieces as they see fit. Or we could pass two specific combinations and backend would chose among them. (perhaps two combinations and uncombined qid, so all existing backends would chose one precombined, and hypothetical other backends could combine itself). This is "only" debug-info related, and typenames are kinda a weak but somewhat preexisting area there. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Wed May 5 23:36:24 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 5 May 2021 21:36:24 +0000 Subject: [M3devel] typename foo.bar vs. bar vs. foo__bar In-Reply-To: References: , Message-ID: Before I make m3front produce "a lot" more heap allocations.. We have for example: INTERFACE Uuio; FROM Ctypes IMPORT int, void_star, const_void_star; FROM Cstddef IMPORT size_t, ssize_t; <*EXTERNAL "Uuio__read"*>PROCEDURE read (d: int; buf: void_star; nbytes: size_t): ssize_t; <*EXTERNAL "Uuio__write"*>PROCEDURE write (d: int; buf: const_void_star; nbytes: size_t): ssize_t; Prior definitions: An id is an integer representing a text. Such as, put them in a hash table when first seen, and append them to a no-delete array and record their offset. A qid is a pair of ids, module and item, where module might be missing. I have this formed now, so that the backend gets qids for the parameter and result names. However in this form, the qids only have one id, the item. No module. That isn't great. Conceptually the code reads: IMPORT Ctypes; IMPORT Cstddef; <*EXTERNAL "Uuio__read"*>PROCEDURE read (d: Ctypes.int; buf: Ctypes.void_star; nbytes: Cstddef.size_t): Cstddef.ssize_t; <*EXTERNAL "Uuio__write"*>PROCEDURE write (d: Ctypes.int; buf: Ctypes.const_void_star; nbytes: Cstddef.size_t): Cstddef.ssize_t; and if all the code was written that way, then nevermind. What to do? The likely easy and working solution is to call Value.GlobalName and then change the qids to TEXTs "Ctypes__int", etc. This will burn "a lot" of extra CPU and do more heap allocation, garbage accumulation, collection, etc. ok? Maybe too little to notice. Another solution is for m3c to consider these to be prefixed with "Uuio.", and I'll fill in the various duplicate typedefs in m3core.h. typedef int Ctypes__int; typedef int Uuio__int; etc. not a big deal. Arguably reasonable, because the types are in scope then in Uuio, and that'd be a full name for them. A kinda bad idea is to allow leaf-only names. Output them as plain "int" and assume any name that matches something in C, is meant to. This would break such things as: INTERFACE A; TYPE int = REAL; INTERFACE B; FROM A IMPORT int; PROCEDURE F():int; My inclination is to just go ahead with producing the extra strings. A compromise (in general) is gate the work on backend mode being C. Passing NIL to the other backends, that do nothing with the information. I could also go through and convert some of the code to use fully qualified types. For my use-case, it isn't much -- just the extern functions in cm3. i.e. "bootstrap is special and the compiler itself does not use the full language"? However a later hypothetically use case could extend beyond cm3. And perhaps beyond extern functions. Though this is increasingly unlikely. As well, the full strings could be formed only for extern. A bit of a hack. - Jay ________________________________ From: Jay K Sent: Wednesday, May 5, 2021 4:46 AM To: m3devel Subject: Re: typename foo.bar vs. bar vs. foo__bar More generally, if it works, I'd just as soon have it always be foo__bar and remove the parameters. For all calls to Value.GlobalName. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 5, 2021 4:45 AM To: m3devel Subject: [M3devel] typename foo.bar vs. bar vs. foo__bar m3c/m3front: Change typenames from: ? modula3/cm3 at c847fcd (github.com) m3c/m3front: Change typenames from: e.g. m3front passes:const_void_star, m3c produces:m3core_Uuio_const_void_star to m3front passes:Ctypes__const_void_star and m3c leaves it alone. Since types have no linkage, there is danger of collision. We'll see. Perhaps this was too cavalier. I have never run the LLVM stuff and m3gdb rarely, sorry. If this breaks either of them, I can make it only diverge if backend mode == C. Or, we could pass the qid to the backends and they could combine the pieces as they see fit. Or we could pass two specific combinations and backend would chose among them. (perhaps two combinations and uncombined qid, so all existing backends would chose one precombined, and hypothetical other backends could combine itself). This is "only" debug-info related, and typenames are kinda a weak but somewhat preexisting area there. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 6 02:00:35 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 6 May 2021 00:00:35 +0000 Subject: [M3devel] typename foo.bar vs. bar vs. foo__bar In-Reply-To: References: , , Message-ID: Well, m3front is a little complicated. Forming the full name is tricky. ?typedef int Uuio__int;? might be best solution. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 5, 2021 2:36:24 PM To: m3devel Subject: Re: [M3devel] typename foo.bar vs. bar vs. foo__bar Before I make m3front produce "a lot" more heap allocations.. We have for example: INTERFACE Uuio; FROM Ctypes IMPORT int, void_star, const_void_star; FROM Cstddef IMPORT size_t, ssize_t; <*EXTERNAL "Uuio__read"*>PROCEDURE read (d: int; buf: void_star; nbytes: size_t): ssize_t; <*EXTERNAL "Uuio__write"*>PROCEDURE write (d: int; buf: const_void_star; nbytes: size_t): ssize_t; Prior definitions: An id is an integer representing a text. Such as, put them in a hash table when first seen, and append them to a no-delete array and record their offset. A qid is a pair of ids, module and item, where module might be missing. I have this formed now, so that the backend gets qids for the parameter and result names. However in this form, the qids only have one id, the item. No module. That isn't great. Conceptually the code reads: IMPORT Ctypes; IMPORT Cstddef; <*EXTERNAL "Uuio__read"*>PROCEDURE read (d: Ctypes.int; buf: Ctypes.void_star; nbytes: Cstddef.size_t): Cstddef.ssize_t; <*EXTERNAL "Uuio__write"*>PROCEDURE write (d: Ctypes.int; buf: Ctypes.const_void_star; nbytes: Cstddef.size_t): Cstddef.ssize_t; and if all the code was written that way, then nevermind. What to do? The likely easy and working solution is to call Value.GlobalName and then change the qids to TEXTs "Ctypes__int", etc. This will burn "a lot" of extra CPU and do more heap allocation, garbage accumulation, collection, etc. ok? Maybe too little to notice. Another solution is for m3c to consider these to be prefixed with "Uuio.", and I'll fill in the various duplicate typedefs in m3core.h. typedef int Ctypes__int; typedef int Uuio__int; etc. not a big deal. Arguably reasonable, because the types are in scope then in Uuio, and that'd be a full name for them. A kinda bad idea is to allow leaf-only names. Output them as plain "int" and assume any name that matches something in C, is meant to. This would break such things as: INTERFACE A; TYPE int = REAL; INTERFACE B; FROM A IMPORT int; PROCEDURE F():int; My inclination is to just go ahead with producing the extra strings. A compromise (in general) is gate the work on backend mode being C. Passing NIL to the other backends, that do nothing with the information. I could also go through and convert some of the code to use fully qualified types. For my use-case, it isn't much -- just the extern functions in cm3. i.e. "bootstrap is special and the compiler itself does not use the full language"? However a later hypothetically use case could extend beyond cm3. And perhaps beyond extern functions. Though this is increasingly unlikely. As well, the full strings could be formed only for extern. A bit of a hack. - Jay ________________________________ From: Jay K Sent: Wednesday, May 5, 2021 4:46 AM To: m3devel Subject: Re: typename foo.bar vs. bar vs. foo__bar More generally, if it works, I'd just as soon have it always be foo__bar and remove the parameters. For all calls to Value.GlobalName. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 5, 2021 4:45 AM To: m3devel Subject: [M3devel] typename foo.bar vs. bar vs. foo__bar m3c/m3front: Change typenames from: ? modula3/cm3 at c847fcd (github.com) m3c/m3front: Change typenames from: e.g. m3front passes:const_void_star, m3c produces:m3core_Uuio_const_void_star to m3front passes:Ctypes__const_void_star and m3c leaves it alone. Since types have no linkage, there is danger of collision. We'll see. Perhaps this was too cavalier. I have never run the LLVM stuff and m3gdb rarely, sorry. If this breaks either of them, I can make it only diverge if backend mode == C. Or, we could pass the qid to the backends and they could combine the pieces as they see fit. Or we could pass two specific combinations and backend would chose among them. (perhaps two combinations and uncombined qid, so all existing backends would chose one precombined, and hypothetical other backends could combine itself). This is "only" debug-info related, and typenames are kinda a weak but somewhat preexisting area there. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From hendrik at topoi.pooq.com Thu May 6 02:02:37 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Wed, 5 May 2021 20:02:37 -0400 Subject: [M3devel] Origins Message-ID: <20210506000237.qudz47j2cgl3fola@topoi.pooq.com> Just curious. What was Modula 3 written in before it becae self-hosting? -- hendrik From vvm at tut.by Thu May 6 07:49:04 2021 From: vvm at tut.by (vvm at tut.by) Date: Thu, 06 May 2021 08:49:04 +0300 Subject: [M3devel] Origins In-Reply-To: <20210506000237.qudz47j2cgl3fola@topoi.pooq.com> References: <20210506000237.qudz47j2cgl3fola@topoi.pooq.com> Message-ID: <1196041620279911@mail.yandex.by> An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 6 09:57:25 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 6 May 2021 07:57:25 +0000 Subject: [M3devel] Origins In-Reply-To: <1196041620279911@mail.yandex.by> References: <20210506000237.qudz47j2cgl3fola@topoi.pooq.com>, <1196041620279911@mail.yandex.by> Message-ID: Henrik means, the implementation not the language definition. Was there a compiler in C? I suspect so. In the 3.6 release, m3build/quake were in C. In my mind, there is kinda a "sharp" tradeoff, between bootstrap (use C or C++) and "dogfooding" (use Modula-3). I realize others will strongly disagree, that it should obviously be one way, or another, or maybe yet another. I think I found a pre-3.6 release where the m3cg layer did not exist, and m3front output C a little more directly. Presumably the boot Makefiles worked at some point too? - Jay ________________________________ From: M3devel on behalf of vvm at tut.by Sent: Thursday, May 6, 2021 5:49 AM To: Hendrik Boom ; m3devel Subject: Re: [M3devel] Origins Hi! I think first M3 is rebranded Modula-2+. Site Archive.org countain [Nelson91] book. But I don't sure that it describe history of Modula-3 genealogy. Best regards, Victor Miasnikov 06.05.2021, 03:03, "Hendrik Boom" : Just curious. What was Modula 3 written in before it becae self-hosting? -- hendrik _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From antony.hosking at anu.edu.au Thu May 6 10:10:02 2021 From: antony.hosking at anu.edu.au (Tony Hosking) Date: Thu, 6 May 2021 08:10:02 +0000 Subject: [M3devel] Origins In-Reply-To: References: <20210506000237.qudz47j2cgl3fola@topoi.pooq.com> <1196041620279911@mail.yandex.by> Message-ID: <14B0B4FE-3C5F-433A-816A-03E8B84E5418@anu.edu.au> It started out in Modula-2+. From: M3devel on behalf of Jay K Date: Thursday, May 6, 2021 at 5:57 PM To: "vvm at tut.by" , Hendrik Boom , m3devel Subject: Re: [M3devel] Origins Henrik means, the implementation not the language definition. Was there a compiler in C? I suspect so. In the 3.6 release, m3build/quake were in C. In my mind, there is kinda a "sharp" tradeoff, between bootstrap (use C or C++) and "dogfooding" (use Modula-3). I realize others will strongly disagree, that it should obviously be one way, or another, or maybe yet another. I think I found a pre-3.6 release where the m3cg layer did not exist, and m3front output C a little more directly. Presumably the boot Makefiles worked at some point too? - Jay ________________________________ From: M3devel on behalf of vvm at tut.by Sent: Thursday, May 6, 2021 5:49 AM To: Hendrik Boom ; m3devel Subject: Re: [M3devel] Origins Hi! I think first M3 is rebranded Modula-2+. Site Archive.org countain [Nelson91] book. But I don't sure that it describe history of Modula-3 genealogy. Best regards, Victor Miasnikov 06.05.2021, 03:03, "Hendrik Boom" : Just curious. What was Modula 3 written in before it becae self-hosting? -- hendrik _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From vvm at tut.by Thu May 6 14:38:34 2021 From: vvm at tut.by (vvm at tut.by) Date: Thu, 06 May 2021 15:38:34 +0300 Subject: [M3devel] Mike Powell's Modula-2 compiler ( a-ka DECWRL Modula-2 Compiler) as "early Modula-3" Re: Origins In-Reply-To: References: <20210506000237.qudz47j2cgl3fola@topoi.pooq.com>, <1196041620279911@mail.yandex.by> Message-ID: <1579741620302889@mail.yandex.by> An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 6 22:31:47 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 6 May 2021 20:31:47 +0000 Subject: [M3devel] Origins In-Reply-To: References: <20210506000237.qudz47j2cgl3fola@topoi.pooq.com> <1196041620279911@mail.yandex.by> , Message-ID: The question remains then, what, if any, backend preceded the gcc backend? Again, I think SRC Modula-3 2.x did output C from m3front. I saw it somewhere. Definitely quake and the "builder" were in C. The 3.6 bootstrap process had per-system assembly to build "m3front" and C for "m3build". I understand the rest, but times change. Unix and C and C++, even C++11, are ubiquitous. For bootstrap ease, C++ is a no-brainer, but the opposite, for dogfooding, using The language is an also no-brainer. Really not just C++. The optional safety might be a problem, and writing the garbage collector, but Go, Rust, Python, Java, JavaScript would all be sensible bootstrap purposes for convenience. I am not sure most of the languages provide the optional safety though. Or global variables, etc. In fact it is tempting to compile to Go and then use its garbage collector. Go is imho clearly the spiritually closest language to Modula-3 -- optionally safe, garbage collector, compiles to native. Not too many systems share those characteristics. > compile it by hand. I never quite understood this. I mean, I do technically. But 1. compile to what? One assembly? C? 2. How many times? Incrementally? In Zig I believe their plan is: A partial Zig compiler in C++. Enough to compile the Zig compiler. A full Zig compiler in Zig. Forever sticking to the subset compilable by previous. - Jay ________________________________ From: Mika Nystrom Sent: Thursday, May 6, 2021 6:46 PM To: Jay K Cc: vvm at tut.by ; Hendrik Boom ; m3devel Subject: Re: [M3devel] Origins Jay you're right that there was an early Modula-3 compiler that generated C. That compiler was not, however, what was used to build the original system and bootstrap the language. I think it was used for porting Modula-3 to non-DEC systems, well, specifically Unix systems, where C is obviously available. I am pretty sure that the systems language (ABI language or whatever you want to call it) on the WRL systems (Topaz?) that first hosted Modula-3 was Modula-2+ and not C. Not sure they used C at all on those machines. So it wouldn't have made sense to go through C... The classic approach is obviously to write the compiler in the target language and compile it by hand. But maybe Modula-3 is a bit too complicated for that! It's easy with Pascal, though---. You can use Pascal to bootstrap Modula-2, and Modula-2+ is upward compatible with Modula-2, so now you have your Modula-2+ self-hosting compiler written. From Modula-2+ to Modula-3 is only a small step. DEC had antipathies against C. If they had used a regular DEC language for the first implementation, it would have been BLISS, not C. But what would have been the point when WRL was already adept at Modula-2+? The Olivetti team that worked on m3tk I suspect were Cedar guys? From PARC? I have VMS 1.x source code somewhere, it's a thick stack of microfiche. It's all in a mixture of VAX MACRO and BLISS-32. There was a time when C wasn't everyone's systems language... Mika On Thu, May 6, 2021 at 12:57 AM Jay K > wrote: Henrik means, the implementation not the language definition. Was there a compiler in C? I suspect so. In the 3.6 release, m3build/quake were in C. In my mind, there is kinda a "sharp" tradeoff, between bootstrap (use C or C++) and "dogfooding" (use Modula-3). I realize others will strongly disagree, that it should obviously be one way, or another, or maybe yet another. I think I found a pre-3.6 release where the m3cg layer did not exist, and m3front output C a little more directly. Presumably the boot Makefiles worked at some point too? - Jay ________________________________ From: M3devel > on behalf of vvm at tut.by > Sent: Thursday, May 6, 2021 5:49 AM To: Hendrik Boom >; m3devel > Subject: Re: [M3devel] Origins Hi! I think first M3 is rebranded Modula-2+. Site Archive.org countain [Nelson91] book. But I don't sure that it describe history of Modula-3 genealogy. Best regards, Victor Miasnikov 06.05.2021, 03:03, "Hendrik Boom" >: Just curious. What was Modula 3 written in before it becae self-hosting? -- hendrik _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Fri May 7 02:59:44 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Thu, 6 May 2021 19:59:44 -0500 Subject: [M3devel] Origins In-Reply-To: References: <20210506000237.qudz47j2cgl3fola@topoi.pooq.com> <1196041620279911@mail.yandex.by> Message-ID: On 5/6/21 3:31 PM, Jay K wrote: > The question remains then, what, if any, backend preceded the gcc backend? > Again, I think SRC Modula-3 2.x did output C from m3front. I saw it somewhere. > Definitely quake and the "builder" were in C. > > The 3.6 bootstrap process had per-system assembly to build "m3front" and C for "m3build". > > I understand the rest, but times change. > Unix and C and C++, even C++11, are ubiquitous. > For bootstrap ease, C++ is a no-brainer, but the opposite, for dogfooding, using The language is an also no-brainer. > > Really not just C++. The optional safety might be a problem, and writing the garbage collector, but Go, Rust, Python, Java, JavaScript would all be sensible bootstrap purposes for convenience. I am not sure most of the languages provide the optional safety though. Or global variables, etc. > In fact it is tempting to compile to Go and then use its garbage collector. > Go is imho clearly the spiritually closest language to Modula-3 -- optionally safe, garbage collector, compiles to native. > Not too many systems share those characteristics. > > ?> compile it by hand. > > I never quite understood this. I mean, I do technically. > But 1. compile to what? One assembly? C? 2. How many times? Incrementally? > > In Zig I believe their plan is: > ?A partial Zig compiler in C++. Enough to compile the Zig compiler. > ?A full Zig compiler in Zig. Forever sticking to the subset compilable by previous. > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Mika Nystrom > *Sent:* Thursday, May 6, 2021 6:46 PM > *To:* Jay K > *Cc:* vvm at tut.by ; Hendrik Boom ; m3devel > *Subject:* Re: [M3devel] Origins > Jay you're right that there was an early Modula-3 compiler that generated C.? That compiler was not, however, what was used to build the original system and bootstrap the language.? I think it was used for porting Modula-3 to non-DEC systems, well, specifically Unix systems, where C is obviously available. > > I am pretty sure that the systems language (ABI language or whatever you want to call it) on the WRL systems (Topaz?) that first hosted Modula-3 was Modula-2+ and not C.? Not sure they used C at all on those machines.? So it wouldn't have made sense to go through C... > > The classic approach is obviously to write the compiler in the target language and compile it by hand.? But maybe Modula-3 is a bit too complicated for that!? It's easy with Pascal, though---.? You can use Pascal to bootstrap Modula-2, and Modula-2+ is upward compatible with Modula-2, so now you have your Modula-2+ self-hosting compiler written.? From Modula-2+ to Modula-3 is only a small step. > DEC had antipathies against C.? If they had used a regular DEC language for the first implementation, it would have been BLISS, not C.? But what would have been the point when WRL was already adept at Modula-2+? > > The Olivetti team that worked on m3tk I suspect were Cedar guys?? From PARC? > > I have VMS 1.x source code somewhere, it's a thick stack of microfiche.? It's all in a mixture of VAX MACRO and BLISS-32. > > There was a time when C wasn't everyone's systems language... In fact, around that time, every company was using their own separate systems language. AT&T did it too. They called it C. > > ? ? ? Mika > > On Thu, May 6, 2021 at 12:57 AM Jay K > wrote: > > Henrik means, the implementation not the language definition. > Was there a compiler in C? I suspect so. > > In the 3.6 release, m3build/quake were in C. > > In my mind, there is kinda a "sharp" tradeoff, between bootstrap (use C or C++) and "dogfooding" (use Modula-3). > I realize others will strongly disagree, that it should obviously be one way, or another, or maybe yet another. > > I think I found a pre-3.6 release where the m3cg layer did not exist, and m3front output C a little more directly. > Presumably the boot Makefiles worked at some point too? > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* M3devel > on behalf of vvm at tut.by > > *Sent:* Thursday, May 6, 2021 5:49 AM > *To:* Hendrik Boom >; m3devel > > *Subject:* Re: [M3devel] Origins > Hi! > > > ? I think first M3 is rebranded Modula-2+. > > Site Archive.org countain [Nelson91] book. > But I don't sure that it describe history of Modula-3 genealogy. > > Best regards, Victor Miasnikov > > 06.05.2021, 03:03, "Hendrik Boom" >: > > Just curious. What was Modula 3 written in before it becae self-hosting? > > -- hendrik > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Sat May 8 02:25:27 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 8 May 2021 00:25:27 +0000 Subject: [M3devel] integer vs. int32/int64 in backend Message-ID: Um, given use of builtin types: PROCEDURE F1():INTEGER; backends are not given "INTEGER" per se, they are given the lower level CG type Int32, Int64, etc. So for example, 32bit and 64bit vary. (There are other ways 32bit and 64bit vary; I am not eliminating this in general; m3front still does layout. But I need portable function declarations, is the thing.) At some risk of, vastly increased indirection, I'd like to treat these as named types. i.e. "INTEGER". A first order implementation, would heap allocate NamedType.P for "every" occurence of "INTEGER". As well as later additional function calls and record field reads. My proposal is somewhere in between. Do just one allocation for each of the builtin types. But still then traffic in NamedType instead of Type. I'm still fleshing out the change to m3front but I think I have it mostly figured out. I have a feeling this will be slower, but probably nobody will notice. I could probably do a more localized hack where I simply..the thing is, I don't need a NamedType.P per se, I just need the m3id of "integer" for import_procedure and declare_param. Very small linear searches would probably suffice. i.e. the dilema is whether to store the qid in the type, or to look it up on the side where needed. I do not in general like look ups on the side. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sat May 8 03:02:42 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 8 May 2021 01:02:42 +0000 Subject: [M3devel] integer vs. int32/int64 in backend In-Reply-To: References: Message-ID: I wasn't able to quickly get the "wrap all types in NamedType" approach to work, but adding a name:M3ID to Type.Info works easily. This costs 4 or 8 bytes in all types. This could be reduced to always 4 presumably. Including in NamedTypes where it goes unused. The QID then could be replaced with just module:M3ID.T. The resulting that NamedTypes do not grow in size. I'll probably do that. I know this is wasteful but it is probably actually ok? Really this is probably better in some ways than the alternative. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Saturday, May 8, 2021 12:25 AM To: m3devel Subject: [M3devel] integer vs. int32/int64 in backend Um, given use of builtin types: PROCEDURE F1():INTEGER; backends are not given "INTEGER" per se, they are given the lower level CG type Int32, Int64, etc. So for example, 32bit and 64bit vary. (There are other ways 32bit and 64bit vary; I am not eliminating this in general; m3front still does layout. But I need portable function declarations, is the thing.) At some risk of, vastly increased indirection, I'd like to treat these as named types. i.e. "INTEGER". A first order implementation, would heap allocate NamedType.P for "every" occurence of "INTEGER". As well as later additional function calls and record field reads. My proposal is somewhere in between. Do just one allocation for each of the builtin types. But still then traffic in NamedType instead of Type. I'm still fleshing out the change to m3front but I think I have it mostly figured out. I have a feeling this will be slower, but probably nobody will notice. I could probably do a more localized hack where I simply..the thing is, I don't need a NamedType.P per se, I just need the m3id of "integer" for import_procedure and declare_param. Very small linear searches would probably suffice. i.e. the dilema is whether to store the qid in the type, or to look it up on the side where needed. I do not in general like look ups on the side. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sat May 8 03:32:42 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 8 May 2021 01:32:42 +0000 Subject: [M3devel] integer vs. int32/int64 in backend In-Reply-To: References: , Message-ID: i.e.: m3front: Provide a qid to backend for non-named types. by jaykrell ? Pull Request #424 ? modula3/cm3 (github.com) Type.Name should probably use this then but that is a larger change and not clearly useful. Not a big deal, I guess, there are only six calls to Type.Name. - Jay ________________________________ From: Jay K Sent: Saturday, May 8, 2021 1:02 AM To: m3devel Subject: Re: integer vs. int32/int64 in backend I wasn't able to quickly get the "wrap all types in NamedType" approach to work, but adding a name:M3ID to Type.Info works easily. This costs 4 or 8 bytes in all types. This could be reduced to always 4 presumably. Including in NamedTypes where it goes unused. The QID then could be replaced with just module:M3ID.T. The resulting that NamedTypes do not grow in size. I'll probably do that. I know this is wasteful but it is probably actually ok? Really this is probably better in some ways than the alternative. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Saturday, May 8, 2021 12:25 AM To: m3devel Subject: [M3devel] integer vs. int32/int64 in backend Um, given use of builtin types: PROCEDURE F1():INTEGER; backends are not given "INTEGER" per se, they are given the lower level CG type Int32, Int64, etc. So for example, 32bit and 64bit vary. (There are other ways 32bit and 64bit vary; I am not eliminating this in general; m3front still does layout. But I need portable function declarations, is the thing.) At some risk of, vastly increased indirection, I'd like to treat these as named types. i.e. "INTEGER". A first order implementation, would heap allocate NamedType.P for "every" occurence of "INTEGER". As well as later additional function calls and record field reads. My proposal is somewhere in between. Do just one allocation for each of the builtin types. But still then traffic in NamedType instead of Type. I'm still fleshing out the change to m3front but I think I have it mostly figured out. I have a feeling this will be slower, but probably nobody will notice. I could probably do a more localized hack where I simply..the thing is, I don't need a NamedType.P per se, I just need the m3id of "integer" for import_procedure and declare_param. Very small linear searches would probably suffice. i.e. the dilema is whether to store the qid in the type, or to look it up on the side where needed. I do not in general like look ups on the side. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sat May 8 07:30:11 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 8 May 2021 05:30:11 +0000 Subject: [M3devel] repeated inline implemenations of lists in m3front? Message-ID: It bothers me that m3front is littered with inline linked list implementations. Here is one of many: PROCEDURE Insert (o: Value.T) = VAR t := top; BEGIN ... (* finally, do the insertion *) IF (o.scope = NIL) THEN o.scope := t END; IF (t.tail # NIL) THEN t.tail.next := o; ELSE t.head := o; END; t.tail := o; INC (t.n_elts); END Insert; I recognize that these are "intrusive lists". "intrusive lists" I think people have less experience with, and less experience genericizing. I do have plenty of experience with them, type-unsafe, in C, and type-safe, genericized/templatized, in C++. But I am not sure what to do here. The C form is implied bad as I indicated -- type-unsafe. The templatization in C++ does not clearly translate into other languages. The way it looks like: Start with C: struct LIST_ENTRY; typedef struct LIST_ENTRY LIST_ENTRY; struct LIST_ENTRY { LIST_ENTRY* Flink; // ForwardLink LIST_ENTRY* Blink; // BackwardLink }; The list is circular. The list head and elements are of the same type. An empty list looks like: LIST_ENTRY head = {&head, &head}; There are functions for append at end, prepend at start, remove, etc. Then, the simple case, the links are at the start of the element: struct Person { LIST_ENTRY* Links; int age; char* first_name; char* last_name; // etc. }; LIST_ENTRY AllPeople; However it generalizes. An element can be on multiple lists: struct Person { LIST_ENTRY* AllPeopleLink; LIST_ENTRY* MaleLink; LIST_ENTRY* FemalesLink; int age; char* first_name; char* last_name; // etc. }; LIST_ENTRY AllPeople; LIST_ENTRY Males; LIST_ENTRY Femails; and then the clever thing is when you iterate: // something like: #define CONTAINING_RECORD(type, field, data) ((type* (((char*)data) - offsetof(type, field))) for (maleLink = Males.Flink; maleLink != &Mails; maleLink = maleLink->Flink) { Person* p = CONTAINING_RECORD(Person, MaleLink, maleLink); ... } You have to know ahead of time what all lists a data will be on. Allocating an element can fail, under low memory. But adding an element to a list however, cannot fail, under low memory or other. That is one of the nice properties. Also, they tend to use less memory, since the links are in the elements. And then, to make this easier to use and typesafe, what you do in C++, is the offsetof is the template parameter. You can then do STL-style iteration. It is nice. But then, can/does/how this translate somehow to Modula-3?? Maybe, is the answer, just use a non-intrusive list and don't worry about low memory? (EFI and NT drivers use this stuff. EFI calls CONTAINING_RECORD just CR. EFI spells out ForwardLink and BackwardLink. The low memory behavior is important in drivers.) m3front also has too many hash tables. Thoughts? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sat May 8 08:52:44 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 8 May 2021 06:52:44 +0000 Subject: [M3devel] language evolution? Message-ID: Here are some language features I would like. 1. OBJECTs that are not heap allocated. Given that records can be on the stack or global, and can contain function pointers, why must OBJECTS be heap allocated? The syntax is almost obvious, except that I think OBJECT implies REF. Perhaps we could say "VALUE" to negate that? Or the existing keyword VAL could be repurposed? 2. non-virtual object methods; or merely non-virtual member functions in records. TYPE Person = RECORD birth_year: INTEGER; CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) END; PROCEDURE Person_Age(VAR self: Person): INTEGER = BEGIN RETURN Date.CurrentYear() - self.birth_year; END Person_Age; VAR johnDoe := Person{1980}; IO.Put(Fmt.Int(johnDoe.age()); => equivalent to Person_Age(johnDoe), but using the type of the prefix parameter to disambiguate the short name "age", so I do not have to state the type in the function name over and over. ? This could kinda be considered the same request..if you give records the ability to inherit..you know, how in C++, class and struct are really the same thing. I get that Modula-3 is optionally safe, has no preprocessor, has easier to implement generics -- it is not C++ -- but are these bad features? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From mika at alum.mit.edu Sat May 8 23:30:23 2021 From: mika at alum.mit.edu (Mika Nystrom) Date: Sat, 8 May 2021 14:30:23 -0700 Subject: [M3devel] language evolution? In-Reply-To: References: Message-ID: Hi Jay, Writing here as someone who is not really a language implementer or a compiler person, but someone who regularly writes a bit of Modula-3 code. Let me take your ideas backwards... 2. what do you gain here? I don't think enough to make the complication worthwhile. Is it such a big deal to write "Person.Age(johnDoe)"? So this used to annoy me as well. I learned to live with it, because Modula-3 has some very effective ways of doing shortcuts for Person.Age: -- if you find yourself typing it a lot in a single procedure, module, or block, you can just write CONST Age = Person.Age. If in a module, you can just as well write FROM Person IMPORT Age; Also consider using generics. -- or ask yourself why you care about the indirection. I have never been able to measure a performance difference, and I have tried a few times. I think the design intent is that for performance critical stuff you use records and procedures and OOP gets the flexibility. Do you need a middle ground, really? ---------------- So you ask OK, what we're proposing is optional. But no, not really. One of the super powers of Modula-3 is that it is a language in the same class as C++ but with a comprehensible type system. There are at least two programs in CM3 that depend on understanding (at least the safe subset) of the type system. If you start adding in non-virtual methods, ..... you are really making life difficult for these programs. I don't know how many others may exist. The ones in CM3 are Network Objects, and the Scheme interpreter I wrote ("Modula-3-Scheme"), which both use m3tk to understand basically arbitrary interfaces, and they really do need to understand the way to call methods, and they use the OOP features to add "surrogate objects" (I think that is a design pattern from Gamma et al.? Or is it called a "facade"?) If you add static/final methods you are suddenly making these systems keep track of different types of methods, on top of everything else they need to do. Maybe not a major complication but still it is a complication that I think can be avoided. 1. This is trickier. The javaism of having to NEW all OBJECTs in M3 is indeed a performance issue in many programs. So let's make VAL = REF^-1 (i.e., the inverse of REF). TYPE T = VAL REF U; means T = U. for all U. The only problem with it is... what the heck do you DO with a stack-allocated VAL OBJECT? You can't pass it to anything that expects an OBJECT, taking the address of something is verboten in non-UNSAFE Modula-3. So you will have to declare your target procedures as taking VAR/READONLY x : VAL OBJECT ... parameters. But what of the methods themselves? The initial argument is the object itself. They now take VAR/READONLY self : VAL OBJECT parameters too, I guess? But then you probably break polymorphism? (And if you are OK with breaking polymorphism, see my objections to your suggestion 2. above. If all you want is a RECORD with PROCEDUREs, just do that!) I would like VAL REF for another purpose. To be able to do: TYPE T = VAL OBJECT ... END; VAR aMegOfObjects := NEW(REF ARRAY OF T, 1024 * 1024); I think that is at least as useful as stack-allocated objects. What would be the meaning of TYPE T = VAL BRANDED OBJECT ... T = VAL UNTRACED REF ... At least the former is important because many existing OBJECT types are BRANDED. Yeah this won't work because the methods are messed up. See: TYPE T = OBJECT METHODS hello() := HelloT; END; PROCEDURE HelloT(self : T) = BEGIN IO.Put("hi!\n") END HelloT; now... TYPE U = VAL T; VAR u : U; BEGIN u.hello() (* what happens here? *) END the problem being that HelloT has the wrong type of initial argument. It needs to be of type VAL T, not T. What if you let it be of type T? Then consider... TYPE T = OBJECT METHODS goodbye() := GoodbyeT END; VAR trap : T; PROCEDURE GoodbyeT(me : T) = BEGIN trap := me END GoodbyeT; TYPE U = VAL T; BEGIN VAR u : U; BEGIN u.goodbye() END; (* u is out of scope here *) trap.goodbye() (* ????? *) END The declaration of U parenthesizes inconveniently. TYPE U = (VAL OBJECT METHODS goodbye() END) OVERRIDES goodbye := GoodbyeU END; If you don't override a method when you make a VAL object it must be overridden with NIL, to preclude making references to the stack. I guess you get a separate type hierarchy for VAL OBJECT types from OBJECT types, you can't inherit the methods. Now there is a school of thought in OOP, I know, that holds that inheriting methods is a Bad Idea and dangerous. That school would probably be OK with your idea. It says that it's OK, and a Good Thing, to inherit the method signature, since it is part of the specification. But implementations (i.e. method text) should not be inherited. Are you part of this school? Is there a problem with REF VAL OBJECT? Yeah it becomes ambiguous. Drat... is it (REF VAL) OBJECT = OBJECT or is it (REF (VAL OBJECT)), a REF to a given VAL OBJECT type? These are obviously different concepts. Are you sure you want this? It seems tricky and not that convenient after all. Mika On Fri, May 7, 2021 at 11:53 PM Jay K wrote: > Here are some language features I would like. > > 1. OBJECTs that are not heap allocated. > > Given that records can be on the stack or global, and can contain function > pointers, > why must OBJECTS be heap allocated? > > The syntax is almost obvious, except that I think OBJECT implies REF. > Perhaps we could say "VALUE" to negate that? > Or the existing keyword VAL could be repurposed? > > 2. non-virtual object methods; or merely non-virtual member functions in > records. > > TYPE Person = RECORD > birth_year: INTEGER; > CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) > END; > > > PROCEDURE Person_Age(VAR self: Person): INTEGER = > BEGIN > RETURN Date.CurrentYear() - self.birth_year; > END Person_Age; > > VAR johnDoe := Person{1980}; > > IO.Put(Fmt.Int(johnDoe.age()); > > => equivalent to Person_Age(johnDoe), but using the type > of the prefix parameter to disambiguate the short name "age", > so I do not have to state the type in the function name over and over. > > ? > > This could kinda be considered the same request..if you give records the > ability > to inherit..you know, how in C++, class and struct are really the same > thing. > > I get that Modula-3 is optionally safe, has no preprocessor, has easier > to implement generics -- it is not C++ -- but are these bad features? > > - Jay > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sun May 9 05:28:06 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 9 May 2021 03:28:06 +0000 Subject: [M3devel] language evolution? In-Reply-To: References: , Message-ID: I admit I do not understand the brand feature. I have never seen it elsewhere and I do not know how it translates to C or C++. The language definition is generally too high level for me to understand. I always think in terms of how things translate to C, which is then easily translated to assembly. C++ trivially translates to C. Modula-3 almost does too. In C and C++, names determine type identity, so: struct Age { int value; }; struct Distance { int value; }; are different types. No brand is needed. No structural hashing occurs. Granted, typedefs are transparent, so: typedef int Distance; typedef int Age; is not so "strong". I know the Modula-3 designers debated this and I should reread that chapter. I often wonder if the solution should not be somehow both. We could use name + hash. If you ignore pickles, or at least pickles surviving type renames while retaining structure (Foo => Foov1), and you can afford to store and compare variably lengthed strings, I think this works and wins. Regarding indirection of objects, the problem is the heap allocation and garbage collector pressure. Not the indirection. In C++ we can do something like: Type xstorage; // static or automatic Type* x = &xstorage; // unify with all pointer-wanting code and then just use x. I kinda think objects should be "var", not "ref". To resolve the part about taking address of locals -- because we *can* actually take the address of locals. The problem then becomes, maybe, where can you store them? But records have this problem too. It is true, I guess, I haven't done it, that records + procedures come close, but there are a few critical differences you indicate. - Lack of inheritance. - Convenient vtable initialization. - Calling: jay.fn^.age(jay); yuck! - Size: Given only non-virtual functions, don't store anything. Inheritance is strongly debated either way. It is clearly useful and sensible sometimes (look at m3front!), and maybe it is overused sometimes (look at m3front? ? ). Basically, yes, you really do want: jay.age() no function pointer or vtable pointer instead of Person_Age(jay) or jay.age(jay) or jay.fn^.age(jay) I mean, you know, achieving state.function() (or "object.method()") in "systems programming" is the stuff of countless person years (C++, Java, COM, etc.) and Modula-3 does support it, but it comes with unnecessary heap allocation. As well, you want state.function() to be dynamically dispatched or not. Depending on which function. You know, I mean, C++ strikes imho a very healthy balance here. I know ultimately C++ is complicated, but it is death by a thousand cuts, and we should be able to afford a few? Thinking more on syntax.. INTERFACE Person; TYPE T = RECORD birth_year: INTEGER; END; PROCEDURE Age(VAR self: T):INTEGER; jay := Person.T{1900}; (* not really :) *) jay.Age(); The idea is a value's static type implies its declaring interface as "its" scope. C++ has a similar thing where parameter types imply namespace. Koenig lookup. Though honestly I want to group arbitrarily many types in one interface/module, so that breaks down. (e.g. m3c.m3) I understand the Interface.T idiom but I don't think we should be bound by it. I don't think Modula-3 generally is. It is just a convention. And x.fn means x is first parameter to fn? TYPE Dog = RECORD birth_year: INTEGER; CONST Age = DogAge; END; TYPE Person = RECORD birth_year: INTEGER; CONST Age = PersonAge; END; PROCEDURE DogAge(VAR self: Dog):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year) * 7; END DogAge; PROCEDURE PersonAge(VAR self: Person):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year); END PersonAge; ? That is much more explicit, as to what kind of lookup occurs, and seems convenient enough, and more flexible. I am using "const" here to indicate "not virtual". I think virtual could reuse the object/methods syntax. But I grant maybe these syntaxes should be more similar. Again look at C++, where the syntaxes are the same except for the "virtual". So my syntax is probably too clever. We just need a way in the existing "methods" syntax to indicate non-virtual. And then there is the matter of static member functions. I understand the point, that language changes ripple through the larger language ecosystem. m3front is the primary but not only definition of the language, but in my opinion should not hold language evolution at a stand still. C++ has the same problem -- expression evaluation in debuggers has been a mess. But, then again, it hasn't bothered me much either. You do not really need the expression evaluator in the debugger to handle much of the language. Language aware editors can suffer too, but again, no big deal, language unaware editors suffice. :) We might consider better librarizing m3front?? I understand, having multiple implementations can be seen as good or bad, so converging them to fewer also good and bad. To repeat, sorry, I think despite all the criticism of C++, it really should be considered to borrow some features from. There really are plenty of non-controversial features. Which reminds me, even C99 can introduce variables later in a function, without implying opening a block/scope/indent. I miss that. :( - Jay ________________________________ From: Mika Nystrom Sent: Saturday, May 8, 2021 9:30 PM To: Jay K ; m3devel Subject: Re: [M3devel] language evolution? Hi Jay, Writing here as someone who is not really a language implementer or a compiler person, but someone who regularly writes a bit of Modula-3 code. Let me take your ideas backwards... 2. what do you gain here? I don't think enough to make the complication worthwhile. Is it such a big deal to write "Person.Age(johnDoe)"? So this used to annoy me as well. I learned to live with it, because Modula-3 has some very effective ways of doing shortcuts for Person.Age: -- if you find yourself typing it a lot in a single procedure, module, or block, you can just write CONST Age = Person.Age. If in a module, you can just as well write FROM Person IMPORT Age; Also consider using generics. -- or ask yourself why you care about the indirection. I have never been able to measure a performance difference, and I have tried a few times. I think the design intent is that for performance critical stuff you use records and procedures and OOP gets the flexibility. Do you need a middle ground, really? ---------------- So you ask OK, what we're proposing is optional. But no, not really. One of the super powers of Modula-3 is that it is a language in the same class as C++ but with a comprehensible type system. There are at least two programs in CM3 that depend on understanding (at least the safe subset) of the type system. If you start adding in non-virtual methods, ..... you are really making life difficult for these programs. I don't know how many others may exist. The ones in CM3 are Network Objects, and the Scheme interpreter I wrote ("Modula-3-Scheme"), which both use m3tk to understand basically arbitrary interfaces, and they really do need to understand the way to call methods, and they use the OOP features to add "surrogate objects" (I think that is a design pattern from Gamma et al.? Or is it called a "facade"?) If you add static/final methods you are suddenly making these systems keep track of different types of methods, on top of everything else they need to do. Maybe not a major complication but still it is a complication that I think can be avoided. 1. This is trickier. The javaism of having to NEW all OBJECTs in M3 is indeed a performance issue in many programs. So let's make VAL = REF^-1 (i.e., the inverse of REF). TYPE T = VAL REF U; means T = U. for all U. The only problem with it is... what the heck do you DO with a stack-allocated VAL OBJECT? You can't pass it to anything that expects an OBJECT, taking the address of something is verboten in non-UNSAFE Modula-3. So you will have to declare your target procedures as taking VAR/READONLY x : VAL OBJECT ... parameters. But what of the methods themselves? The initial argument is the object itself. They now take VAR/READONLY self : VAL OBJECT parameters too, I guess? But then you probably break polymorphism? (And if you are OK with breaking polymorphism, see my objections to your suggestion 2. above. If all you want is a RECORD with PROCEDUREs, just do that!) I would like VAL REF for another purpose. To be able to do: TYPE T = VAL OBJECT ... END; VAR aMegOfObjects := NEW(REF ARRAY OF T, 1024 * 1024); I think that is at least as useful as stack-allocated objects. What would be the meaning of TYPE T = VAL BRANDED OBJECT ... T = VAL UNTRACED REF ... At least the former is important because many existing OBJECT types are BRANDED. Yeah this won't work because the methods are messed up. See: TYPE T = OBJECT METHODS hello() := HelloT; END; PROCEDURE HelloT(self : T) = BEGIN IO.Put("hi!\n") END HelloT; now... TYPE U = VAL T; VAR u : U; BEGIN u.hello() (* what happens here? *) END the problem being that HelloT has the wrong type of initial argument. It needs to be of type VAL T, not T. What if you let it be of type T? Then consider... TYPE T = OBJECT METHODS goodbye() := GoodbyeT END; VAR trap : T; PROCEDURE GoodbyeT(me : T) = BEGIN trap := me END GoodbyeT; TYPE U = VAL T; BEGIN VAR u : U; BEGIN u.goodbye() END; (* u is out of scope here *) trap.goodbye() (* ????? *) END The declaration of U parenthesizes inconveniently. TYPE U = (VAL OBJECT METHODS goodbye() END) OVERRIDES goodbye := GoodbyeU END; If you don't override a method when you make a VAL object it must be overridden with NIL, to preclude making references to the stack. I guess you get a separate type hierarchy for VAL OBJECT types from OBJECT types, you can't inherit the methods. Now there is a school of thought in OOP, I know, that holds that inheriting methods is a Bad Idea and dangerous. That school would probably be OK with your idea. It says that it's OK, and a Good Thing, to inherit the method signature, since it is part of the specification. But implementations (i.e. method text) should not be inherited. Are you part of this school? Is there a problem with REF VAL OBJECT? Yeah it becomes ambiguous. Drat... is it (REF VAL) OBJECT = OBJECT or is it (REF (VAL OBJECT)), a REF to a given VAL OBJECT type? These are obviously different concepts. Are you sure you want this? It seems tricky and not that convenient after all. Mika On Fri, May 7, 2021 at 11:53 PM Jay K > wrote: Here are some language features I would like. 1. OBJECTs that are not heap allocated. Given that records can be on the stack or global, and can contain function pointers, why must OBJECTS be heap allocated? The syntax is almost obvious, except that I think OBJECT implies REF. Perhaps we could say "VALUE" to negate that? Or the existing keyword VAL could be repurposed? 2. non-virtual object methods; or merely non-virtual member functions in records. TYPE Person = RECORD birth_year: INTEGER; CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) END; PROCEDURE Person_Age(VAR self: Person): INTEGER = BEGIN RETURN Date.CurrentYear() - self.birth_year; END Person_Age; VAR johnDoe := Person{1980}; IO.Put(Fmt.Int(johnDoe.age()); => equivalent to Person_Age(johnDoe), but using the type of the prefix parameter to disambiguate the short name "age", so I do not have to state the type in the function name over and over. ? This could kinda be considered the same request..if you give records the ability to inherit..you know, how in C++, class and struct are really the same thing. I get that Modula-3 is optionally safe, has no preprocessor, has easier to implement generics -- it is not C++ -- but are these bad features? - Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sun May 9 09:41:53 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 9 May 2021 07:41:53 +0000 Subject: [M3devel] language evolution? In-Reply-To: References: , , Message-ID: Also I must point out something, in my way of thinking. I think more programming should be type-based, not module- or subsystem- based. Interfaces should be primarily containers of types, and types containers of state and associated functions. (at least syntactically; types need not contain function pointers, just that static function dispatch should be driven by the types of parameters esp. the first parameter). Interfaces should not directly contain many functions. Just like interfaces should not directly contain many variables -- global variables. So types need more powers and less restriction. I want to more often say: state.function() and not Interface.function(state) (and esp. not state.functions.function(state)) Even if not all programming can/should be type-based, there should at least be plenty of linguistic power for such programming. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Sunday, May 9, 2021 3:28 AM To: Mika Nystrom ; m3devel Subject: Re: [M3devel] language evolution? I admit I do not understand the brand feature. I have never seen it elsewhere and I do not know how it translates to C or C++. The language definition is generally too high level for me to understand. I always think in terms of how things translate to C, which is then easily translated to assembly. C++ trivially translates to C. Modula-3 almost does too. In C and C++, names determine type identity, so: struct Age { int value; }; struct Distance { int value; }; are different types. No brand is needed. No structural hashing occurs. Granted, typedefs are transparent, so: typedef int Distance; typedef int Age; is not so "strong". I know the Modula-3 designers debated this and I should reread that chapter. I often wonder if the solution should not be somehow both. We could use name + hash. If you ignore pickles, or at least pickles surviving type renames while retaining structure (Foo => Foov1), and you can afford to store and compare variably lengthed strings, I think this works and wins. Regarding indirection of objects, the problem is the heap allocation and garbage collector pressure. Not the indirection. In C++ we can do something like: Type xstorage; // static or automatic Type* x = &xstorage; // unify with all pointer-wanting code and then just use x. I kinda think objects should be "var", not "ref". To resolve the part about taking address of locals -- because we *can* actually take the address of locals. The problem then becomes, maybe, where can you store them? But records have this problem too. It is true, I guess, I haven't done it, that records + procedures come close, but there are a few critical differences you indicate. - Lack of inheritance. - Convenient vtable initialization. - Calling: jay.fn^.age(jay); yuck! - Size: Given only non-virtual functions, don't store anything. Inheritance is strongly debated either way. It is clearly useful and sensible sometimes (look at m3front!), and maybe it is overused sometimes (look at m3front? ? ). Basically, yes, you really do want: jay.age() no function pointer or vtable pointer instead of Person_Age(jay) or jay.age(jay) or jay.fn^.age(jay) I mean, you know, achieving state.function() (or "object.method()") in "systems programming" is the stuff of countless person years (C++, Java, COM, etc.) and Modula-3 does support it, but it comes with unnecessary heap allocation. As well, you want state.function() to be dynamically dispatched or not. Depending on which function. You know, I mean, C++ strikes imho a very healthy balance here. I know ultimately C++ is complicated, but it is death by a thousand cuts, and we should be able to afford a few? Thinking more on syntax.. INTERFACE Person; TYPE T = RECORD birth_year: INTEGER; END; PROCEDURE Age(VAR self: T):INTEGER; jay := Person.T{1900}; (* not really :) *) jay.Age(); The idea is a value's static type implies its declaring interface as "its" scope. C++ has a similar thing where parameter types imply namespace. Koenig lookup. Though honestly I want to group arbitrarily many types in one interface/module, so that breaks down. (e.g. m3c.m3) I understand the Interface.T idiom but I don't think we should be bound by it. I don't think Modula-3 generally is. It is just a convention. And x.fn means x is first parameter to fn? TYPE Dog = RECORD birth_year: INTEGER; CONST Age = DogAge; END; TYPE Person = RECORD birth_year: INTEGER; CONST Age = PersonAge; END; PROCEDURE DogAge(VAR self: Dog):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year) * 7; END DogAge; PROCEDURE PersonAge(VAR self: Person):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year); END PersonAge; ? That is much more explicit, as to what kind of lookup occurs, and seems convenient enough, and more flexible. I am using "const" here to indicate "not virtual". I think virtual could reuse the object/methods syntax. But I grant maybe these syntaxes should be more similar. Again look at C++, where the syntaxes are the same except for the "virtual". So my syntax is probably too clever. We just need a way in the existing "methods" syntax to indicate non-virtual. And then there is the matter of static member functions. I understand the point, that language changes ripple through the larger language ecosystem. m3front is the primary but not only definition of the language, but in my opinion should not hold language evolution at a stand still. C++ has the same problem -- expression evaluation in debuggers has been a mess. But, then again, it hasn't bothered me much either. You do not really need the expression evaluator in the debugger to handle much of the language. Language aware editors can suffer too, but again, no big deal, language unaware editors suffice. :) We might consider better librarizing m3front?? I understand, having multiple implementations can be seen as good or bad, so converging them to fewer also good and bad. To repeat, sorry, I think despite all the criticism of C++, it really should be considered to borrow some features from. There really are plenty of non-controversial features. Which reminds me, even C99 can introduce variables later in a function, without implying opening a block/scope/indent. I miss that. :( - Jay ________________________________ From: Mika Nystrom Sent: Saturday, May 8, 2021 9:30 PM To: Jay K ; m3devel Subject: Re: [M3devel] language evolution? Hi Jay, Writing here as someone who is not really a language implementer or a compiler person, but someone who regularly writes a bit of Modula-3 code. Let me take your ideas backwards... 2. what do you gain here? I don't think enough to make the complication worthwhile. Is it such a big deal to write "Person.Age(johnDoe)"? So this used to annoy me as well. I learned to live with it, because Modula-3 has some very effective ways of doing shortcuts for Person.Age: -- if you find yourself typing it a lot in a single procedure, module, or block, you can just write CONST Age = Person.Age. If in a module, you can just as well write FROM Person IMPORT Age; Also consider using generics. -- or ask yourself why you care about the indirection. I have never been able to measure a performance difference, and I have tried a few times. I think the design intent is that for performance critical stuff you use records and procedures and OOP gets the flexibility. Do you need a middle ground, really? ---------------- So you ask OK, what we're proposing is optional. But no, not really. One of the super powers of Modula-3 is that it is a language in the same class as C++ but with a comprehensible type system. There are at least two programs in CM3 that depend on understanding (at least the safe subset) of the type system. If you start adding in non-virtual methods, ..... you are really making life difficult for these programs. I don't know how many others may exist. The ones in CM3 are Network Objects, and the Scheme interpreter I wrote ("Modula-3-Scheme"), which both use m3tk to understand basically arbitrary interfaces, and they really do need to understand the way to call methods, and they use the OOP features to add "surrogate objects" (I think that is a design pattern from Gamma et al.? Or is it called a "facade"?) If you add static/final methods you are suddenly making these systems keep track of different types of methods, on top of everything else they need to do. Maybe not a major complication but still it is a complication that I think can be avoided. 1. This is trickier. The javaism of having to NEW all OBJECTs in M3 is indeed a performance issue in many programs. So let's make VAL = REF^-1 (i.e., the inverse of REF). TYPE T = VAL REF U; means T = U. for all U. The only problem with it is... what the heck do you DO with a stack-allocated VAL OBJECT? You can't pass it to anything that expects an OBJECT, taking the address of something is verboten in non-UNSAFE Modula-3. So you will have to declare your target procedures as taking VAR/READONLY x : VAL OBJECT ... parameters. But what of the methods themselves? The initial argument is the object itself. They now take VAR/READONLY self : VAL OBJECT parameters too, I guess? But then you probably break polymorphism? (And if you are OK with breaking polymorphism, see my objections to your suggestion 2. above. If all you want is a RECORD with PROCEDUREs, just do that!) I would like VAL REF for another purpose. To be able to do: TYPE T = VAL OBJECT ... END; VAR aMegOfObjects := NEW(REF ARRAY OF T, 1024 * 1024); I think that is at least as useful as stack-allocated objects. What would be the meaning of TYPE T = VAL BRANDED OBJECT ... T = VAL UNTRACED REF ... At least the former is important because many existing OBJECT types are BRANDED. Yeah this won't work because the methods are messed up. See: TYPE T = OBJECT METHODS hello() := HelloT; END; PROCEDURE HelloT(self : T) = BEGIN IO.Put("hi!\n") END HelloT; now... TYPE U = VAL T; VAR u : U; BEGIN u.hello() (* what happens here? *) END the problem being that HelloT has the wrong type of initial argument. It needs to be of type VAL T, not T. What if you let it be of type T? Then consider... TYPE T = OBJECT METHODS goodbye() := GoodbyeT END; VAR trap : T; PROCEDURE GoodbyeT(me : T) = BEGIN trap := me END GoodbyeT; TYPE U = VAL T; BEGIN VAR u : U; BEGIN u.goodbye() END; (* u is out of scope here *) trap.goodbye() (* ????? *) END The declaration of U parenthesizes inconveniently. TYPE U = (VAL OBJECT METHODS goodbye() END) OVERRIDES goodbye := GoodbyeU END; If you don't override a method when you make a VAL object it must be overridden with NIL, to preclude making references to the stack. I guess you get a separate type hierarchy for VAL OBJECT types from OBJECT types, you can't inherit the methods. Now there is a school of thought in OOP, I know, that holds that inheriting methods is a Bad Idea and dangerous. That school would probably be OK with your idea. It says that it's OK, and a Good Thing, to inherit the method signature, since it is part of the specification. But implementations (i.e. method text) should not be inherited. Are you part of this school? Is there a problem with REF VAL OBJECT? Yeah it becomes ambiguous. Drat... is it (REF VAL) OBJECT = OBJECT or is it (REF (VAL OBJECT)), a REF to a given VAL OBJECT type? These are obviously different concepts. Are you sure you want this? It seems tricky and not that convenient after all. Mika On Fri, May 7, 2021 at 11:53 PM Jay K > wrote: Here are some language features I would like. 1. OBJECTs that are not heap allocated. Given that records can be on the stack or global, and can contain function pointers, why must OBJECTS be heap allocated? The syntax is almost obvious, except that I think OBJECT implies REF. Perhaps we could say "VALUE" to negate that? Or the existing keyword VAL could be repurposed? 2. non-virtual object methods; or merely non-virtual member functions in records. TYPE Person = RECORD birth_year: INTEGER; CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) END; PROCEDURE Person_Age(VAR self: Person): INTEGER = BEGIN RETURN Date.CurrentYear() - self.birth_year; END Person_Age; VAR johnDoe := Person{1980}; IO.Put(Fmt.Int(johnDoe.age()); => equivalent to Person_Age(johnDoe), but using the type of the prefix parameter to disambiguate the short name "age", so I do not have to state the type in the function name over and over. ? This could kinda be considered the same request..if you give records the ability to inherit..you know, how in C++, class and struct are really the same thing. I get that Modula-3 is optionally safe, has no preprocessor, has easier to implement generics -- it is not C++ -- but are these bad features? - Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From dmuysers at hotmail.com Sun May 9 09:44:40 2021 From: dmuysers at hotmail.com (Dirk Muysers) Date: Sun, 09 May 2021 09:44:40 +0200 Subject: [M3devel] language evolution? In-Reply-To: References: <,> Message-ID: Brand feature.?Simple to understand: Structural equality. If you brand a ref, it's type will not be equal to any other. On 09/05/2021 05:28:38, Jay K wrote: I admit I do not understand the brand feature. I have never seen it elsewhere and I do not know how it translates to C or C++. The language definition is generally too high level for me to understand. I always think in terms of how things translate to C, which is then easily translated to assembly. C++ trivially translates to C. Modula-3 almost does too. In C and C++, names determine type identity, so: struct Age { ?int value; }; struct Distance { ?int value; }; are different types. No brand is needed. No structural hashing occurs. Granted, typedefs are transparent, so: typedef int Distance; typedef int Age; is not so "strong". I know the Modula-3 designers debated this and I should reread that chapter. I often wonder if the solution should not be somehow both. We could use name + hash. If you ignore pickles, or at least pickles surviving type renames while retaining structure (Foo => Foov1), and you can afford to store and compare variably lengthed strings, I think this works and wins. Regarding indirection of objects, the problem is the heap allocation and garbage collector pressure. Not the indirection. In C++ we can do something like: Type xstorage; // static or automatic Type* x = &xstorage; // unify with all pointer-wanting code and then just use x. I kinda think objects should be "var", not "ref". To resolve the part about taking address of locals -- because we *can* actually take the address of locals. The problem then becomes, maybe, where can you store them? But records have this problem too. It is true, I guess, I haven't done it, that records + procedures come close, but there are a few critical differences you indicate. ?- Lack of inheritance. ?- Convenient vtable initialization. ?- Calling: ? ? jay.fn^.age(jay); ?yuck! ?- Size: ? ?Given only non-virtual functions, don't store anything. Inheritance is strongly debated either way. It is clearly useful and sensible sometimes (look at m3front!), and maybe it is overused sometimes (look at m3front? ? ). Basically, yes, you really do want: ?jay.age() ?no function pointer or vtable pointer instead of ?Person_Age(jay) or ?jay.age(jay) ? or ?jay.fn^.age(jay) I mean, you know, achieving state.function() (or "object.method()") in "systems programming" is the stuff of countless person years (C++, Java, COM, etc.) and Modula-3 does support it, but it comes with unnecessary heap allocation. As well, you want state.function() to be dynamically dispatched or not. Depending on which function. You know, I mean, C++ strikes imho a very healthy balance here. I know ultimately C++ is complicated, but it is death by a thousand cuts, and we should be able to afford a few? Thinking more on syntax.. INTERFACE Person; TYPE T = RECORD ? birth_year: INTEGER; END; PROCEDURE Age(VAR self: T):INTEGER; jay := Person.T{1900}; (* not really :) *) jay.Age(); The idea is a value's static type implies its declaring interface as "its" scope. ?C++ has a similar thing where parameter types imply namespace. Koenig lookup. Though honestly I want to group arbitrarily many types in one interface/module, so that breaks down. (e.g. m3c.m3) I understand the Interface.T idiom but I don't think we should be bound by it. I don't think Modula-3 generally is. It is just a convention. And x.fn means x is first parameter to fn? TYPE Dog = RECORD ? birth_year: INTEGER; ? CONST Age = DogAge; END; TYPE Person = RECORD ? birth_year: INTEGER; ? CONST Age = PersonAge; END; PROCEDURE DogAge(VAR self: Dog):INTEGER = BEGIN ?RETURN (Time.CurrentYear() - self.birth_year) * 7; END DogAge; PROCEDURE PersonAge(VAR self: Person):INTEGER = BEGIN ?RETURN (Time.CurrentYear() - self.birth_year); END PersonAge; ? That is much more explicit, as to what kind of lookup occurs, and seems convenient enough, and more flexible. I am using "const" here to indicate "not virtual". I think virtual could reuse the object/methods syntax. But I grant maybe these syntaxes should be more similar. Again look at C++, where the syntaxes are the same except for the "virtual". So my syntax is probably too clever. We just need a way in the existing "methods" syntax to indicate non-virtual. And then there is the matter of static member functions. I understand the point, that language changes ripple through the larger language ecosystem. m3front is the primary but not only definition of the language, but in my opinion should not hold language evolution at a stand still. C++ has the same problem -- expression evaluation in debuggers has been a mess. But, then again, it hasn't bothered me much either. You do not really need the expression evaluator in the debugger to handle much of the language. Language aware editors can suffer too, but again, no big deal, language unaware editors suffice. :) We might consider better librarizing m3front?? I understand, having multiple implementations can be seen as good or bad, so converging them to fewer also good and bad. To repeat, sorry, I think despite all the criticism of C++, it really should be considered to borrow some features from. There really are plenty of non-controversial features. Which reminds me, even C99 can introduce variables later in a function, without implying opening a block/scope/indent. I miss that. :( ?- Jay From: Mika Nystrom Sent: Saturday, May 8, 2021 9:30 PM To: Jay K ; m3devel Subject: Re: [M3devel] language evolution? ? Hi Jay, Writing here as someone who is not really a language implementer or a compiler person, but someone who regularly writes a bit of Modula-3 code. Let me take your ideas backwards... 2. what do you gain here?? I don't think enough to make the complication worthwhile.? Is it such a big deal to write "Person.Age(johnDoe)"? So this used to annoy me as well.? I learned to live with it, because Modula-3 has some very effective ways of doing shortcuts for Person.Age: -- if you find yourself typing it a lot in a single procedure, module, or block, you can just write CONST Age = Person.Age.? If in a module, you can just as well write FROM Person IMPORT Age;? Also consider using generics. -- or ask yourself why you care about the indirection.? I have never been able to measure a performance difference, and I have tried a few times.?? I think the design intent is that for performance critical stuff you use records and procedures and OOP gets the flexibility.? Do you need a middle ground, really? ---------------- So you ask OK, what we're proposing is optional.? But no, not really.? One of the super powers of Modula-3 is that it is a language in the same class as C++ but with a comprehensible type system.? There are at least two programs in CM3 that depend on understanding (at least the safe subset) of the type system.? If you start adding in non-virtual methods, ..... you are really making life difficult for these programs.? I don't know how many others may exist.? The ones in CM3 are Network Objects, and the Scheme interpreter I wrote ("Modula-3-Scheme"), which both use m3tk to understand basically arbitrary interfaces, and they really do need to understand the way to call methods, and they use the OOP features to add "surrogate objects" (I think that is a design pattern from Gamma et al.?? Or is it called a "facade"?)? If you add static/final methods you are suddenly making these systems keep track of different types of methods, on top of everything else they need to do.? Maybe not a major complication but still it is a complication that I think can be avoided. 1. This is trickier.? The javaism of having to NEW all OBJECTs in M3 is indeed a performance issue in many programs. So let's make VAL = REF^-1 (i.e., the inverse of REF). TYPE T = VAL REF U; means T = U. for all U. The only problem with it is... what the heck do you DO with a stack-allocated VAL OBJECT?? You can't pass it to anything that expects? an OBJECT, taking the address of something is verboten in non-UNSAFE Modula-3.? So you will have to declare your target procedures as taking VAR/READONLY x : VAL OBJECT ... parameters. But what of the methods themselves?? The initial argument is the object itself.? They now take VAR/READONLY self : VAL OBJECT parameters too, I guess?? But then you probably break polymorphism?? (And if you are OK with breaking polymorphism, see my objections to your suggestion 2. above.? If all you want is a RECORD with PROCEDUREs, just do that!) I would like VAL REF for another purpose.? To be able to do: TYPE T = VAL OBJECT ... END; VAR aMegOfObjects := NEW(REF ARRAY OF T, 1024 * 1024); I think that is at least as useful as stack-allocated objects. What would be the meaning of TYPE T = VAL BRANDED OBJECT ... ? ? ? ? ? T = VAL UNTRACED REF ... At least the former is important because many existing OBJECT types are BRANDED. Yeah this won't work because the methods are messed up.? See: TYPE T = OBJECT METHODS hello() := HelloT; END; PROCEDURE HelloT(self : T) = BEGIN IO.Put("hi!\n") END HelloT; now... TYPE U = VAL T; VAR? ? ? u : U; BEGIN ? ? u.hello()? ?(* what happens here? *) END the problem being that HelloT has the wrong type of initial argument.? It needs to be of type VAL T, not T. What if you let it be of type T? Then consider... TYPE T = OBJECT METHODS goodbye() := GoodbyeT END; VAR trap : T; PROCEDURE GoodbyeT(me : T) = BEGIN trap := me END GoodbyeT; TYPE U = VAL T; BEGIN ? VAR? ? ? ? u : U; ? BEGIN ? ? ? u.goodbye() ? END; (* u is out of scope here *) ? trap.goodbye() (* ????? *) END The declaration of U?parenthesizes inconveniently. TYPE U = (VAL OBJECT METHODS goodbye() END) OVERRIDES goodbye := GoodbyeU END; If you don't override a method when you make a VAL object it must be overridden with NIL, to preclude making references to the stack. I guess you get a separate type hierarchy for VAL OBJECT types from OBJECT types, you can't inherit the methods. Now there is a school of thought in OOP, I know, that holds that inheriting methods is a Bad Idea and dangerous.? That school would probably be OK with your idea.? It says that it's OK, and a Good Thing, to inherit the method signature, since it is part of the specification.? But implementations (i.e. method text) should not be inherited.? Are you part of this school? Is there a problem with REF VAL OBJECT?? Yeah it becomes ambiguous.? Drat... is it (REF VAL) OBJECT = OBJECT or is it (REF (VAL OBJECT)), a REF to a given VAL OBJECT type?? These are obviously different concepts. Are you sure you want this?? It seems tricky and not that convenient after all. ? ? Mika ?? On Fri, May 7, 2021 at 11:53 PM Jay K wrote: Here are some language features I would like. 1. OBJECTs that are not heap allocated. Given that records can be on the stack or global, and can contain function pointers, why must OBJECTS be heap allocated? The syntax is almost obvious, except that I think OBJECT implies REF. Perhaps we could say "VALUE" to negate that? Or the existing keyword VAL could be repurposed? 2. non-virtual object methods; or merely non-virtual member functions in records. TYPE Person = RECORD ? ?birth_year: INTEGER; ? ?CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) ?END; PROCEDURE Person_Age(VAR self: Person): INTEGER = BEGIN ? RETURN Date.CurrentYear() - self.birth_year; END Person_Age; VAR johnDoe := Person{1980}; IO.Put(Fmt.Int(johnDoe.age()); ?=> equivalent to Person_Age(johnDoe), but using the type ? ? of the prefix parameter to disambiguate the short name "age", ? ? so I do not have to state the type in the function name over and over. ? This could kinda be considered the same request..if you give records the ability to inherit..you know, how in C++, class and struct are really the same thing. I get that Modula-3 is optionally safe, has no preprocessor, has easier to implement generics -- it is not C++ -- but are these bad features? ?- Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com [mailto:M3devel at elegosoft.com] https://m3lists.elegosoft.com/mailman/listinfo/m3devel [https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C17f07d7758ab483aa07208d912688496%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637561062376734060%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=wszOKbNR1vJgvnMUDfUBTiVrdHfzbL4st3%2B3RxJFtXs%3D&reserved=0] -------------- next part -------------- An HTML attachment was scrubbed... URL: From dmuysers at hotmail.com Sun May 9 09:45:53 2021 From: dmuysers at hotmail.com (Dirk Muysers) Date: Sun, 09 May 2021 09:45:53 +0200 Subject: [M3devel] language evolution? In-Reply-To: References: <,> <,> Message-ID: Do you want to destroy the language? On 09/05/2021 09:42:25, Jay K wrote: Also I must point out something, in my way of thinking. I think more programming should be type-based, not module- or subsystem- based. Interfaces should be primarily containers of types, and types containers of state and associated functions. (at least syntactically; types need not contain function pointers, just that static function dispatch should be driven by the types of parameters esp. the first parameter). Interfaces should not directly contain many functions. Just like interfaces should not directly contain many variables -- global variables. So types need more powers and less restriction. I want to more often say: state.function() and not Interface.function(state) (and esp. not state.functions.function(state)) Even if not all programming can/should be type-based, there should at least be plenty of linguistic power for such programming. - Jay From: M3devel on behalf of Jay K Sent: Sunday, May 9, 2021 3:28 AM To: Mika Nystrom ; m3devel Subject: Re: [M3devel] language evolution? ? I admit I do not understand the brand feature. I have never seen it elsewhere and I do not know how it translates to C or C++. The language definition is generally too high level for me to understand. I always think in terms of how things translate to C, which is then easily translated to assembly. C++ trivially translates to C. Modula-3 almost does too. In C and C++, names determine type identity, so: struct Age { ?int value; }; struct Distance { ?int value; }; are different types. No brand is needed. No structural hashing occurs. Granted, typedefs are transparent, so: typedef int Distance; typedef int Age; is not so "strong". I know the Modula-3 designers debated this and I should reread that chapter. I often wonder if the solution should not be somehow both. We could use name + hash. If you ignore pickles, or at least pickles surviving type renames while retaining structure (Foo => Foov1), and you can afford to store and compare variably lengthed strings, I think this works and wins. Regarding indirection of objects, the problem is the heap allocation and garbage collector pressure. Not the indirection. In C++ we can do something like: Type xstorage; // static or automatic Type* x = &xstorage; // unify with all pointer-wanting code and then just use x. I kinda think objects should be "var", not "ref". To resolve the part about taking address of locals -- because we *can* actually take the address of locals. The problem then becomes, maybe, where can you store them? But records have this problem too. It is true, I guess, I haven't done it, that records + procedures come close, but there are a few critical differences you indicate. ?- Lack of inheritance. ?- Convenient vtable initialization. ?- Calling: ? ? jay.fn^.age(jay); ?yuck! ?- Size: ? ?Given only non-virtual functions, don't store anything. Inheritance is strongly debated either way. It is clearly useful and sensible sometimes (look at m3front!), and maybe it is overused sometimes (look at m3front? ? ). Basically, yes, you really do want: ?jay.age() ?no function pointer or vtable pointer instead of ?Person_Age(jay) or ?jay.age(jay) ? or ?jay.fn^.age(jay) I mean, you know, achieving state.function() (or "object.method()") in "systems programming" is the stuff of countless person years (C++, Java, COM, etc.) and Modula-3 does support it, but it comes with unnecessary heap allocation. As well, you want state.function() to be dynamically dispatched or not. Depending on which function. You know, I mean, C++ strikes imho a very healthy balance here. I know ultimately C++ is complicated, but it is death by a thousand cuts, and we should be able to afford a few? Thinking more on syntax.. INTERFACE Person; TYPE T = RECORD ? birth_year: INTEGER; END; PROCEDURE Age(VAR self: T):INTEGER; jay := Person.T{1900}; (* not really :) *) jay.Age(); The idea is a value's static type implies its declaring interface as "its" scope. ?C++ has a similar thing where parameter types imply namespace. Koenig lookup. Though honestly I want to group arbitrarily many types in one interface/module, so that breaks down. (e.g. m3c.m3) I understand the Interface.T idiom but I don't think we should be bound by it. I don't think Modula-3 generally is. It is just a convention. And x.fn means x is first parameter to fn? TYPE Dog = RECORD ? birth_year: INTEGER; ? CONST Age = DogAge; END; TYPE Person = RECORD ? birth_year: INTEGER; ? CONST Age = PersonAge; END; PROCEDURE DogAge(VAR self: Dog):INTEGER = BEGIN ?RETURN (Time.CurrentYear() - self.birth_year) * 7; END DogAge; PROCEDURE PersonAge(VAR self: Person):INTEGER = BEGIN ?RETURN (Time.CurrentYear() - self.birth_year); END PersonAge; ? That is much more explicit, as to what kind of lookup occurs, and seems convenient enough, and more flexible. I am using "const" here to indicate "not virtual". I think virtual could reuse the object/methods syntax. But I grant maybe these syntaxes should be more similar. Again look at C++, where the syntaxes are the same except for the "virtual". So my syntax is probably too clever. We just need a way in the existing "methods" syntax to indicate non-virtual. And then there is the matter of static member functions. I understand the point, that language changes ripple through the larger language ecosystem. m3front is the primary but not only definition of the language, but in my opinion should not hold language evolution at a stand still. C++ has the same problem -- expression evaluation in debuggers has been a mess. But, then again, it hasn't bothered me much either. You do not really need the expression evaluator in the debugger to handle much of the language. Language aware editors can suffer too, but again, no big deal, language unaware editors suffice. :) We might consider better librarizing m3front?? I understand, having multiple implementations can be seen as good or bad, so converging them to fewer also good and bad. To repeat, sorry, I think despite all the criticism of C++, it really should be considered to borrow some features from. There really are plenty of non-controversial features. Which reminds me, even C99 can introduce variables later in a function, without implying opening a block/scope/indent. I miss that. :( ?- Jay From: Mika Nystrom Sent: Saturday, May 8, 2021 9:30 PM To: Jay K ; m3devel Subject: Re: [M3devel] language evolution? ? Hi Jay, Writing here as someone who is not really a language implementer or a compiler person, but someone who regularly writes a bit of Modula-3 code. Let me take your ideas backwards... 2. what do you gain here?? I don't think enough to make the complication worthwhile.? Is it such a big deal to write "Person.Age(johnDoe)"? So this used to annoy me as well.? I learned to live with it, because Modula-3 has some very effective ways of doing shortcuts for Person.Age: -- if you find yourself typing it a lot in a single procedure, module, or block, you can just write CONST Age = Person.Age.? If in a module, you can just as well write FROM Person IMPORT Age;? Also consider using generics. -- or ask yourself why you care about the indirection.? I have never been able to measure a performance difference, and I have tried a few times.?? I think the design intent is that for performance critical stuff you use records and procedures and OOP gets the flexibility.? Do you need a middle ground, really? ---------------- So you ask OK, what we're proposing is optional.? But no, not really.? One of the super powers of Modula-3 is that it is a language in the same class as C++ but with a comprehensible type system.? There are at least two programs in CM3 that depend on understanding (at least the safe subset) of the type system.? If you start adding in non-virtual methods, ..... you are really making life difficult for these programs.? I don't know how many others may exist.? The ones in CM3 are Network Objects, and the Scheme interpreter I wrote ("Modula-3-Scheme"), which both use m3tk to understand basically arbitrary interfaces, and they really do need to understand the way to call methods, and they use the OOP features to add "surrogate objects" (I think that is a design pattern from Gamma et al.?? Or is it called a "facade"?)? If you add static/final methods you are suddenly making these systems keep track of different types of methods, on top of everything else they need to do.? Maybe not a major complication but still it is a complication that I think can be avoided. 1. This is trickier.? The javaism of having to NEW all OBJECTs in M3 is indeed a performance issue in many programs. So let's make VAL = REF^-1 (i.e., the inverse of REF). TYPE T = VAL REF U; means T = U. for all U. The only problem with it is... what the heck do you DO with a stack-allocated VAL OBJECT?? You can't pass it to anything that expects? an OBJECT, taking the address of something is verboten in non-UNSAFE Modula-3.? So you will have to declare your target procedures as taking VAR/READONLY x : VAL OBJECT ... parameters. But what of the methods themselves?? The initial argument is the object itself.? They now take VAR/READONLY self : VAL OBJECT parameters too, I guess?? But then you probably break polymorphism?? (And if you are OK with breaking polymorphism, see my objections to your suggestion 2. above.? If all you want is a RECORD with PROCEDUREs, just do that!) I would like VAL REF for another purpose.? To be able to do: TYPE T = VAL OBJECT ... END; VAR aMegOfObjects := NEW(REF ARRAY OF T, 1024 * 1024); I think that is at least as useful as stack-allocated objects. What would be the meaning of TYPE T = VAL BRANDED OBJECT ... ? ? ? ? ? T = VAL UNTRACED REF ... At least the former is important because many existing OBJECT types are BRANDED. Yeah this won't work because the methods are messed up.? See: TYPE T = OBJECT METHODS hello() := HelloT; END; PROCEDURE HelloT(self : T) = BEGIN IO.Put("hi!\n") END HelloT; now... TYPE U = VAL T; VAR? ? ? u : U; BEGIN ? ? u.hello()? ?(* what happens here? *) END the problem being that HelloT has the wrong type of initial argument.? It needs to be of type VAL T, not T. What if you let it be of type T? Then consider... TYPE T = OBJECT METHODS goodbye() := GoodbyeT END; VAR trap : T; PROCEDURE GoodbyeT(me : T) = BEGIN trap := me END GoodbyeT; TYPE U = VAL T; BEGIN ? VAR? ? ? ? u : U; ? BEGIN ? ? ? u.goodbye() ? END; (* u is out of scope here *) ? trap.goodbye() (* ????? *) END The declaration of U?parenthesizes inconveniently. TYPE U = (VAL OBJECT METHODS goodbye() END) OVERRIDES goodbye := GoodbyeU END; If you don't override a method when you make a VAL object it must be overridden with NIL, to preclude making references to the stack. I guess you get a separate type hierarchy for VAL OBJECT types from OBJECT types, you can't inherit the methods. Now there is a school of thought in OOP, I know, that holds that inheriting methods is a Bad Idea and dangerous.? That school would probably be OK with your idea.? It says that it's OK, and a Good Thing, to inherit the method signature, since it is part of the specification.? But implementations (i.e. method text) should not be inherited.? Are you part of this school? Is there a problem with REF VAL OBJECT?? Yeah it becomes ambiguous.? Drat... is it (REF VAL) OBJECT = OBJECT or is it (REF (VAL OBJECT)), a REF to a given VAL OBJECT type?? These are obviously different concepts. Are you sure you want this?? It seems tricky and not that convenient after all. ? ? Mika ?? On Fri, May 7, 2021 at 11:53 PM Jay K wrote: Here are some language features I would like. 1. OBJECTs that are not heap allocated. Given that records can be on the stack or global, and can contain function pointers, why must OBJECTS be heap allocated? The syntax is almost obvious, except that I think OBJECT implies REF. Perhaps we could say "VALUE" to negate that? Or the existing keyword VAL could be repurposed? 2. non-virtual object methods; or merely non-virtual member functions in records. TYPE Person = RECORD ? ?birth_year: INTEGER; ? ?CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) ?END; PROCEDURE Person_Age(VAR self: Person): INTEGER = BEGIN ? RETURN Date.CurrentYear() - self.birth_year; END Person_Age; VAR johnDoe := Person{1980}; IO.Put(Fmt.Int(johnDoe.age()); ?=> equivalent to Person_Age(johnDoe), but using the type ? ? of the prefix parameter to disambiguate the short name "age", ? ? so I do not have to state the type in the function name over and over. ? This could kinda be considered the same request..if you give records the ability to inherit..you know, how in C++, class and struct are really the same thing. I get that Modula-3 is optionally safe, has no preprocessor, has easier to implement generics -- it is not C++ -- but are these bad features? ?- Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com [mailto:M3devel at elegosoft.com] https://m3lists.elegosoft.com/mailman/listinfo/m3devel [https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cf984b3a099a641c7e51308d9129a8707%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637561277174836574%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=hCoJRiZkVXriTEBAXFb%2FQt86n12J78tfPtGc2E2ZzVA%3D&reserved=0] -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sun May 9 09:46:21 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 9 May 2021 07:46:21 +0000 Subject: [M3devel] language evolution? In-Reply-To: References: <,> , Message-ID: Does it have a runtime/codegen impact? - Jay ________________________________ From: Dirk Muysers Sent: Sunday, May 9, 2021 7:44 AM To: Jay K ; Mika Nystrom ; m3devel at elegosoft.com Subject: Re: [M3devel] language evolution? Brand feature. Simple to understand: Structural equality. If you brand a ref, it's type will not be equal to any other. On 09/05/2021 05:28:38, Jay K wrote: I admit I do not understand the brand feature. I have never seen it elsewhere and I do not know how it translates to C or C++. The language definition is generally too high level for me to understand. I always think in terms of how things translate to C, which is then easily translated to assembly. C++ trivially translates to C. Modula-3 almost does too. In C and C++, names determine type identity, so: struct Age { int value; }; struct Distance { int value; }; are different types. No brand is needed. No structural hashing occurs. Granted, typedefs are transparent, so: typedef int Distance; typedef int Age; is not so "strong". I know the Modula-3 designers debated this and I should reread that chapter. I often wonder if the solution should not be somehow both. We could use name + hash. If you ignore pickles, or at least pickles surviving type renames while retaining structure (Foo => Foov1), and you can afford to store and compare variably lengthed strings, I think this works and wins. Regarding indirection of objects, the problem is the heap allocation and garbage collector pressure. Not the indirection. In C++ we can do something like: Type xstorage; // static or automatic Type* x = &xstorage; // unify with all pointer-wanting code and then just use x. I kinda think objects should be "var", not "ref". To resolve the part about taking address of locals -- because we *can* actually take the address of locals. The problem then becomes, maybe, where can you store them? But records have this problem too. It is true, I guess, I haven't done it, that records + procedures come close, but there are a few critical differences you indicate. - Lack of inheritance. - Convenient vtable initialization. - Calling: jay.fn^.age(jay); yuck! - Size: Given only non-virtual functions, don't store anything. Inheritance is strongly debated either way. It is clearly useful and sensible sometimes (look at m3front!), and maybe it is overused sometimes (look at m3front? ? ). Basically, yes, you really do want: jay.age() no function pointer or vtable pointer instead of Person_Age(jay) or jay.age(jay) or jay.fn^.age(jay) I mean, you know, achieving state.function() (or "object.method()") in "systems programming" is the stuff of countless person years (C++, Java, COM, etc.) and Modula-3 does support it, but it comes with unnecessary heap allocation. As well, you want state.function() to be dynamically dispatched or not. Depending on which function. You know, I mean, C++ strikes imho a very healthy balance here. I know ultimately C++ is complicated, but it is death by a thousand cuts, and we should be able to afford a few? Thinking more on syntax.. INTERFACE Person; TYPE T = RECORD birth_year: INTEGER; END; PROCEDURE Age(VAR self: T):INTEGER; jay := Person.T{1900}; (* not really :) *) jay.Age(); The idea is a value's static type implies its declaring interface as "its" scope. C++ has a similar thing where parameter types imply namespace. Koenig lookup. Though honestly I want to group arbitrarily many types in one interface/module, so that breaks down. (e.g. m3c.m3) I understand the Interface.T idiom but I don't think we should be bound by it. I don't think Modula-3 generally is. It is just a convention. And x.fn means x is first parameter to fn? TYPE Dog = RECORD birth_year: INTEGER; CONST Age = DogAge; END; TYPE Person = RECORD birth_year: INTEGER; CONST Age = PersonAge; END; PROCEDURE DogAge(VAR self: Dog):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year) * 7; END DogAge; PROCEDURE PersonAge(VAR self: Person):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year); END PersonAge; ? That is much more explicit, as to what kind of lookup occurs, and seems convenient enough, and more flexible. I am using "const" here to indicate "not virtual". I think virtual could reuse the object/methods syntax. But I grant maybe these syntaxes should be more similar. Again look at C++, where the syntaxes are the same except for the "virtual". So my syntax is probably too clever. We just need a way in the existing "methods" syntax to indicate non-virtual. And then there is the matter of static member functions. I understand the point, that language changes ripple through the larger language ecosystem. m3front is the primary but not only definition of the language, but in my opinion should not hold language evolution at a stand still. C++ has the same problem -- expression evaluation in debuggers has been a mess. But, then again, it hasn't bothered me much either. You do not really need the expression evaluator in the debugger to handle much of the language. Language aware editors can suffer too, but again, no big deal, language unaware editors suffice. :) We might consider better librarizing m3front?? I understand, having multiple implementations can be seen as good or bad, so converging them to fewer also good and bad. To repeat, sorry, I think despite all the criticism of C++, it really should be considered to borrow some features from. There really are plenty of non-controversial features. Which reminds me, even C99 can introduce variables later in a function, without implying opening a block/scope/indent. I miss that. :( - Jay ________________________________ From: Mika Nystrom Sent: Saturday, May 8, 2021 9:30 PM To: Jay K ; m3devel Subject: Re: [M3devel] language evolution? Hi Jay, Writing here as someone who is not really a language implementer or a compiler person, but someone who regularly writes a bit of Modula-3 code. Let me take your ideas backwards... 2. what do you gain here? I don't think enough to make the complication worthwhile. Is it such a big deal to write "Person.Age(johnDoe)"? So this used to annoy me as well. I learned to live with it, because Modula-3 has some very effective ways of doing shortcuts for Person.Age: -- if you find yourself typing it a lot in a single procedure, module, or block, you can just write CONST Age = Person.Age. If in a module, you can just as well write FROM Person IMPORT Age; Also consider using generics. -- or ask yourself why you care about the indirection. I have never been able to measure a performance difference, and I have tried a few times. I think the design intent is that for performance critical stuff you use records and procedures and OOP gets the flexibility. Do you need a middle ground, really? ---------------- So you ask OK, what we're proposing is optional. But no, not really. One of the super powers of Modula-3 is that it is a language in the same class as C++ but with a comprehensible type system. There are at least two programs in CM3 that depend on understanding (at least the safe subset) of the type system. If you start adding in non-virtual methods, ..... you are really making life difficult for these programs. I don't know how many others may exist. The ones in CM3 are Network Objects, and the Scheme interpreter I wrote ("Modula-3-Scheme"), which both use m3tk to understand basically arbitrary interfaces, and they really do need to understand the way to call methods, and they use the OOP features to add "surrogate objects" (I think that is a design pattern from Gamma et al.? Or is it called a "facade"?) If you add static/final methods you are suddenly making these systems keep track of different types of methods, on top of everything else they need to do. Maybe not a major complication but still it is a complication that I think can be avoided. 1. This is trickier. The javaism of having to NEW all OBJECTs in M3 is indeed a performance issue in many programs. So let's make VAL = REF^-1 (i.e., the inverse of REF). TYPE T = VAL REF U; means T = U. for all U. The only problem with it is... what the heck do you DO with a stack-allocated VAL OBJECT? You can't pass it to anything that expects an OBJECT, taking the address of something is verboten in non-UNSAFE Modula-3. So you will have to declare your target procedures as taking VAR/READONLY x : VAL OBJECT ... parameters. But what of the methods themselves? The initial argument is the object itself. They now take VAR/READONLY self : VAL OBJECT parameters too, I guess? But then you probably break polymorphism? (And if you are OK with breaking polymorphism, see my objections to your suggestion 2. above. If all you want is a RECORD with PROCEDUREs, just do that!) I would like VAL REF for another purpose. To be able to do: TYPE T = VAL OBJECT ... END; VAR aMegOfObjects := NEW(REF ARRAY OF T, 1024 * 1024); I think that is at least as useful as stack-allocated objects. What would be the meaning of TYPE T = VAL BRANDED OBJECT ... T = VAL UNTRACED REF ... At least the former is important because many existing OBJECT types are BRANDED. Yeah this won't work because the methods are messed up. See: TYPE T = OBJECT METHODS hello() := HelloT; END; PROCEDURE HelloT(self : T) = BEGIN IO.Put("hi!\n") END HelloT; now... TYPE U = VAL T; VAR u : U; BEGIN u.hello() (* what happens here? *) END the problem being that HelloT has the wrong type of initial argument. It needs to be of type VAL T, not T. What if you let it be of type T? Then consider... TYPE T = OBJECT METHODS goodbye() := GoodbyeT END; VAR trap : T; PROCEDURE GoodbyeT(me : T) = BEGIN trap := me END GoodbyeT; TYPE U = VAL T; BEGIN VAR u : U; BEGIN u.goodbye() END; (* u is out of scope here *) trap.goodbye() (* ????? *) END The declaration of U parenthesizes inconveniently. TYPE U = (VAL OBJECT METHODS goodbye() END) OVERRIDES goodbye := GoodbyeU END; If you don't override a method when you make a VAL object it must be overridden with NIL, to preclude making references to the stack. I guess you get a separate type hierarchy for VAL OBJECT types from OBJECT types, you can't inherit the methods. Now there is a school of thought in OOP, I know, that holds that inheriting methods is a Bad Idea and dangerous. That school would probably be OK with your idea. It says that it's OK, and a Good Thing, to inherit the method signature, since it is part of the specification. But implementations (i.e. method text) should not be inherited. Are you part of this school? Is there a problem with REF VAL OBJECT? Yeah it becomes ambiguous. Drat... is it (REF VAL) OBJECT = OBJECT or is it (REF (VAL OBJECT)), a REF to a given VAL OBJECT type? These are obviously different concepts. Are you sure you want this? It seems tricky and not that convenient after all. Mika On Fri, May 7, 2021 at 11:53 PM Jay K > wrote: Here are some language features I would like. 1. OBJECTs that are not heap allocated. Given that records can be on the stack or global, and can contain function pointers, why must OBJECTS be heap allocated? The syntax is almost obvious, except that I think OBJECT implies REF. Perhaps we could say "VALUE" to negate that? Or the existing keyword VAL could be repurposed? 2. non-virtual object methods; or merely non-virtual member functions in records. TYPE Person = RECORD birth_year: INTEGER; CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) END; PROCEDURE Person_Age(VAR self: Person): INTEGER = BEGIN RETURN Date.CurrentYear() - self.birth_year; END Person_Age; VAR johnDoe := Person{1980}; IO.Put(Fmt.Int(johnDoe.age()); => equivalent to Person_Age(johnDoe), but using the type of the prefix parameter to disambiguate the short name "age", so I do not have to state the type in the function name over and over. ? This could kinda be considered the same request..if you give records the ability to inherit..you know, how in C++, class and struct are really the same thing. I get that Modula-3 is optionally safe, has no preprocessor, has easier to implement generics -- it is not C++ -- but are these bad features? - Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sun May 9 09:51:36 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 9 May 2021 07:51:36 +0000 Subject: [M3devel] language evolution? In-Reply-To: References: <,> <,> , Message-ID: I am not suggesting remove anything. Interfaces/modules must retain their existing features. Global variables, etc. But I'd like types to gain features. Specifically as I said: I should not have to heap allocate state in order to say state.function() nor should state.function() require a function pointer (or vtable pointer). Nor would I like to say state.function(state). Is that too much to ask? - Jay ________________________________ From: Dirk Muysers Sent: Sunday, May 9, 2021 7:45 AM To: Jay K ; Mika Nystrom ; m3devel at elegosoft.com Subject: Re: [M3devel] language evolution? Do you want to destroy the language? On 09/05/2021 09:42:25, Jay K wrote: Also I must point out something, in my way of thinking. I think more programming should be type-based, not module- or subsystem- based. Interfaces should be primarily containers of types, and types containers of state and associated functions. (at least syntactically; types need not contain function pointers, just that static function dispatch should be driven by the types of parameters esp. the first parameter). Interfaces should not directly contain many functions. Just like interfaces should not directly contain many variables -- global variables. So types need more powers and less restriction. I want to more often say: state.function() and not Interface.function(state) (and esp. not state.functions.function(state)) Even if not all programming can/should be type-based, there should at least be plenty of linguistic power for such programming. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Sunday, May 9, 2021 3:28 AM To: Mika Nystrom ; m3devel Subject: Re: [M3devel] language evolution? I admit I do not understand the brand feature. I have never seen it elsewhere and I do not know how it translates to C or C++. The language definition is generally too high level for me to understand. I always think in terms of how things translate to C, which is then easily translated to assembly. C++ trivially translates to C. Modula-3 almost does too. In C and C++, names determine type identity, so: struct Age { int value; }; struct Distance { int value; }; are different types. No brand is needed. No structural hashing occurs. Granted, typedefs are transparent, so: typedef int Distance; typedef int Age; is not so "strong". I know the Modula-3 designers debated this and I should reread that chapter. I often wonder if the solution should not be somehow both. We could use name + hash. If you ignore pickles, or at least pickles surviving type renames while retaining structure (Foo => Foov1), and you can afford to store and compare variably lengthed strings, I think this works and wins. Regarding indirection of objects, the problem is the heap allocation and garbage collector pressure. Not the indirection. In C++ we can do something like: Type xstorage; // static or automatic Type* x = &xstorage; // unify with all pointer-wanting code and then just use x. I kinda think objects should be "var", not "ref". To resolve the part about taking address of locals -- because we *can* actually take the address of locals. The problem then becomes, maybe, where can you store them? But records have this problem too. It is true, I guess, I haven't done it, that records + procedures come close, but there are a few critical differences you indicate. - Lack of inheritance. - Convenient vtable initialization. - Calling: jay.fn^.age(jay); yuck! - Size: Given only non-virtual functions, don't store anything. Inheritance is strongly debated either way. It is clearly useful and sensible sometimes (look at m3front!), and maybe it is overused sometimes (look at m3front? ? ). Basically, yes, you really do want: jay.age() no function pointer or vtable pointer instead of Person_Age(jay) or jay.age(jay) or jay.fn^.age(jay) I mean, you know, achieving state.function() (or "object.method()") in "systems programming" is the stuff of countless person years (C++, Java, COM, etc.) and Modula-3 does support it, but it comes with unnecessary heap allocation. As well, you want state.function() to be dynamically dispatched or not. Depending on which function. You know, I mean, C++ strikes imho a very healthy balance here. I know ultimately C++ is complicated, but it is death by a thousand cuts, and we should be able to afford a few? Thinking more on syntax.. INTERFACE Person; TYPE T = RECORD birth_year: INTEGER; END; PROCEDURE Age(VAR self: T):INTEGER; jay := Person.T{1900}; (* not really :) *) jay.Age(); The idea is a value's static type implies its declaring interface as "its" scope. C++ has a similar thing where parameter types imply namespace. Koenig lookup. Though honestly I want to group arbitrarily many types in one interface/module, so that breaks down. (e.g. m3c.m3) I understand the Interface.T idiom but I don't think we should be bound by it. I don't think Modula-3 generally is. It is just a convention. And x.fn means x is first parameter to fn? TYPE Dog = RECORD birth_year: INTEGER; CONST Age = DogAge; END; TYPE Person = RECORD birth_year: INTEGER; CONST Age = PersonAge; END; PROCEDURE DogAge(VAR self: Dog):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year) * 7; END DogAge; PROCEDURE PersonAge(VAR self: Person):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year); END PersonAge; ? That is much more explicit, as to what kind of lookup occurs, and seems convenient enough, and more flexible. I am using "const" here to indicate "not virtual". I think virtual could reuse the object/methods syntax. But I grant maybe these syntaxes should be more similar. Again look at C++, where the syntaxes are the same except for the "virtual". So my syntax is probably too clever. We just need a way in the existing "methods" syntax to indicate non-virtual. And then there is the matter of static member functions. I understand the point, that language changes ripple through the larger language ecosystem. m3front is the primary but not only definition of the language, but in my opinion should not hold language evolution at a stand still. C++ has the same problem -- expression evaluation in debuggers has been a mess. But, then again, it hasn't bothered me much either. You do not really need the expression evaluator in the debugger to handle much of the language. Language aware editors can suffer too, but again, no big deal, language unaware editors suffice. :) We might consider better librarizing m3front?? I understand, having multiple implementations can be seen as good or bad, so converging them to fewer also good and bad. To repeat, sorry, I think despite all the criticism of C++, it really should be considered to borrow some features from. There really are plenty of non-controversial features. Which reminds me, even C99 can introduce variables later in a function, without implying opening a block/scope/indent. I miss that. :( - Jay ________________________________ From: Mika Nystrom Sent: Saturday, May 8, 2021 9:30 PM To: Jay K ; m3devel Subject: Re: [M3devel] language evolution? Hi Jay, Writing here as someone who is not really a language implementer or a compiler person, but someone who regularly writes a bit of Modula-3 code. Let me take your ideas backwards... 2. what do you gain here? I don't think enough to make the complication worthwhile. Is it such a big deal to write "Person.Age(johnDoe)"? So this used to annoy me as well. I learned to live with it, because Modula-3 has some very effective ways of doing shortcuts for Person.Age: -- if you find yourself typing it a lot in a single procedure, module, or block, you can just write CONST Age = Person.Age. If in a module, you can just as well write FROM Person IMPORT Age; Also consider using generics. -- or ask yourself why you care about the indirection. I have never been able to measure a performance difference, and I have tried a few times. I think the design intent is that for performance critical stuff you use records and procedures and OOP gets the flexibility. Do you need a middle ground, really? ---------------- So you ask OK, what we're proposing is optional. But no, not really. One of the super powers of Modula-3 is that it is a language in the same class as C++ but with a comprehensible type system. There are at least two programs in CM3 that depend on understanding (at least the safe subset) of the type system. If you start adding in non-virtual methods, ..... you are really making life difficult for these programs. I don't know how many others may exist. The ones in CM3 are Network Objects, and the Scheme interpreter I wrote ("Modula-3-Scheme"), which both use m3tk to understand basically arbitrary interfaces, and they really do need to understand the way to call methods, and they use the OOP features to add "surrogate objects" (I think that is a design pattern from Gamma et al.? Or is it called a "facade"?) If you add static/final methods you are suddenly making these systems keep track of different types of methods, on top of everything else they need to do. Maybe not a major complication but still it is a complication that I think can be avoided. 1. This is trickier. The javaism of having to NEW all OBJECTs in M3 is indeed a performance issue in many programs. So let's make VAL = REF^-1 (i.e., the inverse of REF). TYPE T = VAL REF U; means T = U. for all U. The only problem with it is... what the heck do you DO with a stack-allocated VAL OBJECT? You can't pass it to anything that expects an OBJECT, taking the address of something is verboten in non-UNSAFE Modula-3. So you will have to declare your target procedures as taking VAR/READONLY x : VAL OBJECT ... parameters. But what of the methods themselves? The initial argument is the object itself. They now take VAR/READONLY self : VAL OBJECT parameters too, I guess? But then you probably break polymorphism? (And if you are OK with breaking polymorphism, see my objections to your suggestion 2. above. If all you want is a RECORD with PROCEDUREs, just do that!) I would like VAL REF for another purpose. To be able to do: TYPE T = VAL OBJECT ... END; VAR aMegOfObjects := NEW(REF ARRAY OF T, 1024 * 1024); I think that is at least as useful as stack-allocated objects. What would be the meaning of TYPE T = VAL BRANDED OBJECT ... T = VAL UNTRACED REF ... At least the former is important because many existing OBJECT types are BRANDED. Yeah this won't work because the methods are messed up. See: TYPE T = OBJECT METHODS hello() := HelloT; END; PROCEDURE HelloT(self : T) = BEGIN IO.Put("hi!\n") END HelloT; now... TYPE U = VAL T; VAR u : U; BEGIN u.hello() (* what happens here? *) END the problem being that HelloT has the wrong type of initial argument. It needs to be of type VAL T, not T. What if you let it be of type T? Then consider... TYPE T = OBJECT METHODS goodbye() := GoodbyeT END; VAR trap : T; PROCEDURE GoodbyeT(me : T) = BEGIN trap := me END GoodbyeT; TYPE U = VAL T; BEGIN VAR u : U; BEGIN u.goodbye() END; (* u is out of scope here *) trap.goodbye() (* ????? *) END The declaration of U parenthesizes inconveniently. TYPE U = (VAL OBJECT METHODS goodbye() END) OVERRIDES goodbye := GoodbyeU END; If you don't override a method when you make a VAL object it must be overridden with NIL, to preclude making references to the stack. I guess you get a separate type hierarchy for VAL OBJECT types from OBJECT types, you can't inherit the methods. Now there is a school of thought in OOP, I know, that holds that inheriting methods is a Bad Idea and dangerous. That school would probably be OK with your idea. It says that it's OK, and a Good Thing, to inherit the method signature, since it is part of the specification. But implementations (i.e. method text) should not be inherited. Are you part of this school? Is there a problem with REF VAL OBJECT? Yeah it becomes ambiguous. Drat... is it (REF VAL) OBJECT = OBJECT or is it (REF (VAL OBJECT)), a REF to a given VAL OBJECT type? These are obviously different concepts. Are you sure you want this? It seems tricky and not that convenient after all. Mika On Fri, May 7, 2021 at 11:53 PM Jay K > wrote: Here are some language features I would like. 1. OBJECTs that are not heap allocated. Given that records can be on the stack or global, and can contain function pointers, why must OBJECTS be heap allocated? The syntax is almost obvious, except that I think OBJECT implies REF. Perhaps we could say "VALUE" to negate that? Or the existing keyword VAL could be repurposed? 2. non-virtual object methods; or merely non-virtual member functions in records. TYPE Person = RECORD birth_year: INTEGER; CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) END; PROCEDURE Person_Age(VAR self: Person): INTEGER = BEGIN RETURN Date.CurrentYear() - self.birth_year; END Person_Age; VAR johnDoe := Person{1980}; IO.Put(Fmt.Int(johnDoe.age()); => equivalent to Person_Age(johnDoe), but using the type of the prefix parameter to disambiguate the short name "age", so I do not have to state the type in the function name over and over. ? This could kinda be considered the same request..if you give records the ability to inherit..you know, how in C++, class and struct are really the same thing. I get that Modula-3 is optionally safe, has no preprocessor, has easier to implement generics -- it is not C++ -- but are these bad features? - Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From dmuysers at hotmail.com Sun May 9 09:57:50 2021 From: dmuysers at hotmail.com (dirk muysers) Date: Sun, 9 May 2021 07:57:50 +0000 Subject: [M3devel] language evolution? In-Reply-To: References: <,> <,> , , Message-ID: Then program in golang. It gives you just that Sent from my iPhone On 9 May 2021, at 09:51, Jay K wrote: ? I am not suggesting remove anything. Interfaces/modules must retain their existing features. Global variables, etc. But I'd like types to gain features. Specifically as I said: I should not have to heap allocate state in order to say state.function() nor should state.function() require a function pointer (or vtable pointer). Nor would I like to say state.function(state). Is that too much to ask? - Jay ________________________________ From: Dirk Muysers Sent: Sunday, May 9, 2021 7:45 AM To: Jay K ; Mika Nystrom ; m3devel at elegosoft.com Subject: Re: [M3devel] language evolution? Do you want to destroy the language? On 09/05/2021 09:42:25, Jay K wrote: Also I must point out something, in my way of thinking. I think more programming should be type-based, not module- or subsystem- based. Interfaces should be primarily containers of types, and types containers of state and associated functions. (at least syntactically; types need not contain function pointers, just that static function dispatch should be driven by the types of parameters esp. the first parameter). Interfaces should not directly contain many functions. Just like interfaces should not directly contain many variables -- global variables. So types need more powers and less restriction. I want to more often say: state.function() and not Interface.function(state) (and esp. not state.functions.function(state)) Even if not all programming can/should be type-based, there should at least be plenty of linguistic power for such programming. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Sunday, May 9, 2021 3:28 AM To: Mika Nystrom ; m3devel Subject: Re: [M3devel] language evolution? I admit I do not understand the brand feature. I have never seen it elsewhere and I do not know how it translates to C or C++. The language definition is generally too high level for me to understand. I always think in terms of how things translate to C, which is then easily translated to assembly. C++ trivially translates to C. Modula-3 almost does too. In C and C++, names determine type identity, so: struct Age { int value; }; struct Distance { int value; }; are different types. No brand is needed. No structural hashing occurs. Granted, typedefs are transparent, so: typedef int Distance; typedef int Age; is not so "strong". I know the Modula-3 designers debated this and I should reread that chapter. I often wonder if the solution should not be somehow both. We could use name + hash. If you ignore pickles, or at least pickles surviving type renames while retaining structure (Foo => Foov1), and you can afford to store and compare variably lengthed strings, I think this works and wins. Regarding indirection of objects, the problem is the heap allocation and garbage collector pressure. Not the indirection. In C++ we can do something like: Type xstorage; // static or automatic Type* x = &xstorage; // unify with all pointer-wanting code and then just use x. I kinda think objects should be "var", not "ref". To resolve the part about taking address of locals -- because we *can* actually take the address of locals. The problem then becomes, maybe, where can you store them? But records have this problem too. It is true, I guess, I haven't done it, that records + procedures come close, but there are a few critical differences you indicate. - Lack of inheritance. - Convenient vtable initialization. - Calling: jay.fn^.age(jay); yuck! - Size: Given only non-virtual functions, don't store anything. Inheritance is strongly debated either way. It is clearly useful and sensible sometimes (look at m3front!), and maybe it is overused sometimes (look at m3front? ? ). Basically, yes, you really do want: jay.age() no function pointer or vtable pointer instead of Person_Age(jay) or jay.age(jay) or jay.fn^.age(jay) I mean, you know, achieving state.function() (or "object.method()") in "systems programming" is the stuff of countless person years (C++, Java, COM, etc.) and Modula-3 does support it, but it comes with unnecessary heap allocation. As well, you want state.function() to be dynamically dispatched or not. Depending on which function. You know, I mean, C++ strikes imho a very healthy balance here. I know ultimately C++ is complicated, but it is death by a thousand cuts, and we should be able to afford a few? Thinking more on syntax.. INTERFACE Person; TYPE T = RECORD birth_year: INTEGER; END; PROCEDURE Age(VAR self: T):INTEGER; jay := Person.T{1900}; (* not really :) *) jay.Age(); The idea is a value's static type implies its declaring interface as "its" scope. C++ has a similar thing where parameter types imply namespace. Koenig lookup. Though honestly I want to group arbitrarily many types in one interface/module, so that breaks down. (e.g. m3c.m3) I understand the Interface.T idiom but I don't think we should be bound by it. I don't think Modula-3 generally is. It is just a convention. And x.fn means x is first parameter to fn? TYPE Dog = RECORD birth_year: INTEGER; CONST Age = DogAge; END; TYPE Person = RECORD birth_year: INTEGER; CONST Age = PersonAge; END; PROCEDURE DogAge(VAR self: Dog):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year) * 7; END DogAge; PROCEDURE PersonAge(VAR self: Person):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year); END PersonAge; ? That is much more explicit, as to what kind of lookup occurs, and seems convenient enough, and more flexible. I am using "const" here to indicate "not virtual". I think virtual could reuse the object/methods syntax. But I grant maybe these syntaxes should be more similar. Again look at C++, where the syntaxes are the same except for the "virtual". So my syntax is probably too clever. We just need a way in the existing "methods" syntax to indicate non-virtual. And then there is the matter of static member functions. I understand the point, that language changes ripple through the larger language ecosystem. m3front is the primary but not only definition of the language, but in my opinion should not hold language evolution at a stand still. C++ has the same problem -- expression evaluation in debuggers has been a mess. But, then again, it hasn't bothered me much either. You do not really need the expression evaluator in the debugger to handle much of the language. Language aware editors can suffer too, but again, no big deal, language unaware editors suffice. :) We might consider better librarizing m3front?? I understand, having multiple implementations can be seen as good or bad, so converging them to fewer also good and bad. To repeat, sorry, I think despite all the criticism of C++, it really should be considered to borrow some features from. There really are plenty of non-controversial features. Which reminds me, even C99 can introduce variables later in a function, without implying opening a block/scope/indent. I miss that. :( - Jay ________________________________ From: Mika Nystrom Sent: Saturday, May 8, 2021 9:30 PM To: Jay K ; m3devel Subject: Re: [M3devel] language evolution? Hi Jay, Writing here as someone who is not really a language implementer or a compiler person, but someone who regularly writes a bit of Modula-3 code. Let me take your ideas backwards... 2. what do you gain here? I don't think enough to make the complication worthwhile. Is it such a big deal to write "Person.Age(johnDoe)"? So this used to annoy me as well. I learned to live with it, because Modula-3 has some very effective ways of doing shortcuts for Person.Age: -- if you find yourself typing it a lot in a single procedure, module, or block, you can just write CONST Age = Person.Age. If in a module, you can just as well write FROM Person IMPORT Age; Also consider using generics. -- or ask yourself why you care about the indirection. I have never been able to measure a performance difference, and I have tried a few times. I think the design intent is that for performance critical stuff you use records and procedures and OOP gets the flexibility. Do you need a middle ground, really? ---------------- So you ask OK, what we're proposing is optional. But no, not really. One of the super powers of Modula-3 is that it is a language in the same class as C++ but with a comprehensible type system. There are at least two programs in CM3 that depend on understanding (at least the safe subset) of the type system. If you start adding in non-virtual methods, ..... you are really making life difficult for these programs. I don't know how many others may exist. The ones in CM3 are Network Objects, and the Scheme interpreter I wrote ("Modula-3-Scheme"), which both use m3tk to understand basically arbitrary interfaces, and they really do need to understand the way to call methods, and they use the OOP features to add "surrogate objects" (I think that is a design pattern from Gamma et al.? Or is it called a "facade"?) If you add static/final methods you are suddenly making these systems keep track of different types of methods, on top of everything else they need to do. Maybe not a major complication but still it is a complication that I think can be avoided. 1. This is trickier. The javaism of having to NEW all OBJECTs in M3 is indeed a performance issue in many programs. So let's make VAL = REF^-1 (i.e., the inverse of REF). TYPE T = VAL REF U; means T = U. for all U. The only problem with it is... what the heck do you DO with a stack-allocated VAL OBJECT? You can't pass it to anything that expects an OBJECT, taking the address of something is verboten in non-UNSAFE Modula-3. So you will have to declare your target procedures as taking VAR/READONLY x : VAL OBJECT ... parameters. But what of the methods themselves? The initial argument is the object itself. They now take VAR/READONLY self : VAL OBJECT parameters too, I guess? But then you probably break polymorphism? (And if you are OK with breaking polymorphism, see my objections to your suggestion 2. above. If all you want is a RECORD with PROCEDUREs, just do that!) I would like VAL REF for another purpose. To be able to do: TYPE T = VAL OBJECT ... END; VAR aMegOfObjects := NEW(REF ARRAY OF T, 1024 * 1024); I think that is at least as useful as stack-allocated objects. What would be the meaning of TYPE T = VAL BRANDED OBJECT ... T = VAL UNTRACED REF ... At least the former is important because many existing OBJECT types are BRANDED. Yeah this won't work because the methods are messed up. See: TYPE T = OBJECT METHODS hello() := HelloT; END; PROCEDURE HelloT(self : T) = BEGIN IO.Put("hi!\n") END HelloT; now... TYPE U = VAL T; VAR u : U; BEGIN u.hello() (* what happens here? *) END the problem being that HelloT has the wrong type of initial argument. It needs to be of type VAL T, not T. What if you let it be of type T? Then consider... TYPE T = OBJECT METHODS goodbye() := GoodbyeT END; VAR trap : T; PROCEDURE GoodbyeT(me : T) = BEGIN trap := me END GoodbyeT; TYPE U = VAL T; BEGIN VAR u : U; BEGIN u.goodbye() END; (* u is out of scope here *) trap.goodbye() (* ????? *) END The declaration of U parenthesizes inconveniently. TYPE U = (VAL OBJECT METHODS goodbye() END) OVERRIDES goodbye := GoodbyeU END; If you don't override a method when you make a VAL object it must be overridden with NIL, to preclude making references to the stack. I guess you get a separate type hierarchy for VAL OBJECT types from OBJECT types, you can't inherit the methods. Now there is a school of thought in OOP, I know, that holds that inheriting methods is a Bad Idea and dangerous. That school would probably be OK with your idea. It says that it's OK, and a Good Thing, to inherit the method signature, since it is part of the specification. But implementations (i.e. method text) should not be inherited. Are you part of this school? Is there a problem with REF VAL OBJECT? Yeah it becomes ambiguous. Drat... is it (REF VAL) OBJECT = OBJECT or is it (REF (VAL OBJECT)), a REF to a given VAL OBJECT type? These are obviously different concepts. Are you sure you want this? It seems tricky and not that convenient after all. Mika On Fri, May 7, 2021 at 11:53 PM Jay K > wrote: Here are some language features I would like. 1. OBJECTs that are not heap allocated. Given that records can be on the stack or global, and can contain function pointers, why must OBJECTS be heap allocated? The syntax is almost obvious, except that I think OBJECT implies REF. Perhaps we could say "VALUE" to negate that? Or the existing keyword VAL could be repurposed? 2. non-virtual object methods; or merely non-virtual member functions in records. TYPE Person = RECORD birth_year: INTEGER; CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) END; PROCEDURE Person_Age(VAR self: Person): INTEGER = BEGIN RETURN Date.CurrentYear() - self.birth_year; END Person_Age; VAR johnDoe := Person{1980}; IO.Put(Fmt.Int(johnDoe.age()); => equivalent to Person_Age(johnDoe), but using the type of the prefix parameter to disambiguate the short name "age", so I do not have to state the type in the function name over and over. ? This could kinda be considered the same request..if you give records the ability to inherit..you know, how in C++, class and struct are really the same thing. I get that Modula-3 is optionally safe, has no preprocessor, has easier to implement generics -- it is not C++ -- but are these bad features? - Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From hendrik at topoi.pooq.com Sun May 9 19:42:55 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Sun, 9 May 2021 13:42:55 -0400 Subject: [M3devel] language evolution? In-Reply-To: References: <,> Message-ID: <20210509174255.xwgkip77omb5chjq@topoi.pooq.com> On Sun, May 09, 2021 at 07:46:21AM +0000, Jay K wrote: > > Which reminds me, even C99 can introduce variables later in a function, without implying > opening a block/scope/indent. I miss that. :( That's one of the few features of modern C that I miss in Modula 3. And, for the record, it originated in Algol 68 as far as I know. It's especially pleasant in algol68 to be able to declare int i := 10; int j := i + 3; thus defining and initializing each of i and j at one go. Or define a constant: int k = j * 2; -- hendrik From jayk123 at hotmail.com Mon May 10 02:38:26 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 10 May 2021 00:38:26 +0000 Subject: [M3devel] language evolution? In-Reply-To: References: , Message-ID: That?s a good point. ?Heap? and ?gc? being implementation details to achieve higher level semantics in the absence of sophisticated static analysis. I.e. ?no use after free? (?free? still being an implementation detail). And yes I do much pine for Rust?s safety without GC. But, again, um, ignoring the ref/var problem..the language already supports this, except er um, that var/ref problem I guess is real. I wonder if the var/ref solution though is that the first parameter could optionally be ?var? except that today means var of ref, so some new annotation would be needed. And then as you say, the usual var restrictions. Go is also wierd/interesting/different?inspiring in that stack/heap allocation really is determined by escape analysis. I was surprised to see that. - Jay ________________________________ From: Mika Nystrom Sent: Sunday, May 9, 2021 2:49 PM To: Jay K Cc: Dirk Muysers; m3devel at elegosoft.com Subject: Re: [M3devel] language evolution? Jay I am with you on the heap allocation vs. not. But did you consider that there is nothing in the language definition that says that OBJECTs must be heap allocated and garbage collected? Maybe you could have a STACK object for which it would simply be an error to assign the object to a variable, same as an inner procedure. Would that do what you need? TYPE T = OBJECT a, b : Field; METHODS m := Method END; VAR staticT : T; VAR stackT := NEW(STACK T); BEGIN P(stackT); (* OK as long as P does not assign its argument to a variable *) stackT.m(); (* OK as long as m does not assign self to a variable *) staticT := stackT; (* checked runtime error *) END STACK is maybe too implementation-centric. INNER T? EPHEMERAL T? leave everything else the same. Is this a baby step towards Rust? Also you STACK could be a pragma. No change to M3 semantics, but stating a restriction on the particular program you're writing. But regarding your other suggestion, static/final/class methods would be a major complication to Modula-3's really simple object type system. I don't think that's a good idea. A thought here: can you optimize out the vtable lookups at link time? Then you could add another pragma, one that says that your methods are <*FINAL*> or whatnot and simply make attempting an OVERRIDE of such a method fail the link step. That way you don't actually change the language at all, no complication to the type system. What you're doing is simply adding a meta-language that describes constraints on how you use the types. You may also find that your compiler will then catch some of the STACK errors from the previous example at compile time (or link time) rather than at runtime (not always, of course). You can do more with a meta-language like that. Invariants of different kinds (see LM3), a lot more than what you propose, but very useful for program verification: https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-72.pdf I have taught programming courses many times and always find it difficult to explain to students why we "professionals" don't annotate machine-readable and relevant invariants in our programs as a matter of course. Mika On Sun, May 9, 2021 at 12:51 AM Jay K > wrote: I am not suggesting remove anything. Interfaces/modules must retain their existing features. Global variables, etc. But I'd like types to gain features. Specifically as I said: I should not have to heap allocate state in order to say state.function() nor should state.function() require a function pointer (or vtable pointer). Nor would I like to say state.function(state). Is that too much to ask? - Jay ________________________________ From: Dirk Muysers > Sent: Sunday, May 9, 2021 7:45 AM To: Jay K >; Mika Nystrom >; m3devel at elegosoft.com > Subject: Re: [M3devel] language evolution? Do you want to destroy the language? On 09/05/2021 09:42:25, Jay K > wrote: Also I must point out something, in my way of thinking. I think more programming should be type-based, not module- or subsystem- based. Interfaces should be primarily containers of types, and types containers of state and associated functions. (at least syntactically; types need not contain function pointers, just that static function dispatch should be driven by the types of parameters esp. the first parameter). Interfaces should not directly contain many functions. Just like interfaces should not directly contain many variables -- global variables. So types need more powers and less restriction. I want to more often say: state.function() and not Interface.function(state) (and esp. not state.functions.function(state)) Even if not all programming can/should be type-based, there should at least be plenty of linguistic power for such programming. - Jay ________________________________ From: M3devel > on behalf of Jay K > Sent: Sunday, May 9, 2021 3:28 AM To: Mika Nystrom >; m3devel > Subject: Re: [M3devel] language evolution? I admit I do not understand the brand feature. I have never seen it elsewhere and I do not know how it translates to C or C++. The language definition is generally too high level for me to understand. I always think in terms of how things translate to C, which is then easily translated to assembly. C++ trivially translates to C. Modula-3 almost does too. In C and C++, names determine type identity, so: struct Age { int value; }; struct Distance { int value; }; are different types. No brand is needed. No structural hashing occurs. Granted, typedefs are transparent, so: typedef int Distance; typedef int Age; is not so "strong". I know the Modula-3 designers debated this and I should reread that chapter. I often wonder if the solution should not be somehow both. We could use name + hash. If you ignore pickles, or at least pickles surviving type renames while retaining structure (Foo => Foov1), and you can afford to store and compare variably lengthed strings, I think this works and wins. Regarding indirection of objects, the problem is the heap allocation and garbage collector pressure. Not the indirection. In C++ we can do something like: Type xstorage; // static or automatic Type* x = &xstorage; // unify with all pointer-wanting code and then just use x. I kinda think objects should be "var", not "ref". To resolve the part about taking address of locals -- because we *can* actually take the address of locals. The problem then becomes, maybe, where can you store them? But records have this problem too. It is true, I guess, I haven't done it, that records + procedures come close, but there are a few critical differences you indicate. - Lack of inheritance. - Convenient vtable initialization. - Calling: jay.fn^.age(jay); yuck! - Size: Given only non-virtual functions, don't store anything. Inheritance is strongly debated either way. It is clearly useful and sensible sometimes (look at m3front!), and maybe it is overused sometimes (look at m3front? ? ). Basically, yes, you really do want: jay.age() no function pointer or vtable pointer instead of Person_Age(jay) or jay.age(jay) or jay.fn^.age(jay) I mean, you know, achieving state.function() (or "object.method()") in "systems programming" is the stuff of countless person years (C++, Java, COM, etc.) and Modula-3 does support it, but it comes with unnecessary heap allocation. As well, you want state.function() to be dynamically dispatched or not. Depending on which function. You know, I mean, C++ strikes imho a very healthy balance here. I know ultimately C++ is complicated, but it is death by a thousand cuts, and we should be able to afford a few? Thinking more on syntax.. INTERFACE Person; TYPE T = RECORD birth_year: INTEGER; END; PROCEDURE Age(VAR self: T):INTEGER; jay := Person.T{1900}; (* not really :) *) jay.Age(); The idea is a value's static type implies its declaring interface as "its" scope. C++ has a similar thing where parameter types imply namespace. Koenig lookup. Though honestly I want to group arbitrarily many types in one interface/module, so that breaks down. (e.g. m3c.m3) I understand the Interface.T idiom but I don't think we should be bound by it. I don't think Modula-3 generally is. It is just a convention. And x.fn means x is first parameter to fn? TYPE Dog = RECORD birth_year: INTEGER; CONST Age = DogAge; END; TYPE Person = RECORD birth_year: INTEGER; CONST Age = PersonAge; END; PROCEDURE DogAge(VAR self: Dog):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year) * 7; END DogAge; PROCEDURE PersonAge(VAR self: Person):INTEGER = BEGIN RETURN (Time.CurrentYear() - self.birth_year); END PersonAge; ? That is much more explicit, as to what kind of lookup occurs, and seems convenient enough, and more flexible. I am using "const" here to indicate "not virtual". I think virtual could reuse the object/methods syntax. But I grant maybe these syntaxes should be more similar. Again look at C++, where the syntaxes are the same except for the "virtual". So my syntax is probably too clever. We just need a way in the existing "methods" syntax to indicate non-virtual. And then there is the matter of static member functions. I understand the point, that language changes ripple through the larger language ecosystem. m3front is the primary but not only definition of the language, but in my opinion should not hold language evolution at a stand still. C++ has the same problem -- expression evaluation in debuggers has been a mess. But, then again, it hasn't bothered me much either. You do not really need the expression evaluator in the debugger to handle much of the language. Language aware editors can suffer too, but again, no big deal, language unaware editors suffice. :) We might consider better librarizing m3front?? I understand, having multiple implementations can be seen as good or bad, so converging them to fewer also good and bad. To repeat, sorry, I think despite all the criticism of C++, it really should be considered to borrow some features from. There really are plenty of non-controversial features. Which reminds me, even C99 can introduce variables later in a function, without implying opening a block/scope/indent. I miss that. :( - Jay ________________________________ From: Mika Nystrom > Sent: Saturday, May 8, 2021 9:30 PM To: Jay K >; m3devel > Subject: Re: [M3devel] language evolution? Hi Jay, Writing here as someone who is not really a language implementer or a compiler person, but someone who regularly writes a bit of Modula-3 code. Let me take your ideas backwards... 2. what do you gain here? I don't think enough to make the complication worthwhile. Is it such a big deal to write "Person.Age(johnDoe)"? So this used to annoy me as well. I learned to live with it, because Modula-3 has some very effective ways of doing shortcuts for Person.Age: -- if you find yourself typing it a lot in a single procedure, module, or block, you can just write CONST Age = Person.Age. If in a module, you can just as well write FROM Person IMPORT Age; Also consider using generics. -- or ask yourself why you care about the indirection. I have never been able to measure a performance difference, and I have tried a few times. I think the design intent is that for performance critical stuff you use records and procedures and OOP gets the flexibility. Do you need a middle ground, really? ---------------- So you ask OK, what we're proposing is optional. But no, not really. One of the super powers of Modula-3 is that it is a language in the same class as C++ but with a comprehensible type system. There are at least two programs in CM3 that depend on understanding (at least the safe subset) of the type system. If you start adding in non-virtual methods, ..... you are really making life difficult for these programs. I don't know how many others may exist. The ones in CM3 are Network Objects, and the Scheme interpreter I wrote ("Modula-3-Scheme"), which both use m3tk to understand basically arbitrary interfaces, and they really do need to understand the way to call methods, and they use the OOP features to add "surrogate objects" (I think that is a design pattern from Gamma et al.? Or is it called a "facade"?) If you add static/final methods you are suddenly making these systems keep track of different types of methods, on top of everything else they need to do. Maybe not a major complication but still it is a complication that I think can be avoided. 1. This is trickier. The javaism of having to NEW all OBJECTs in M3 is indeed a performance issue in many programs. So let's make VAL = REF^-1 (i.e., the inverse of REF). TYPE T = VAL REF U; means T = U. for all U. The only problem with it is... what the heck do you DO with a stack-allocated VAL OBJECT? You can't pass it to anything that expects an OBJECT, taking the address of something is verboten in non-UNSAFE Modula-3. So you will have to declare your target procedures as taking VAR/READONLY x : VAL OBJECT ... parameters. But what of the methods themselves? The initial argument is the object itself. They now take VAR/READONLY self : VAL OBJECT parameters too, I guess? But then you probably break polymorphism? (And if you are OK with breaking polymorphism, see my objections to your suggestion 2. above. If all you want is a RECORD with PROCEDUREs, just do that!) I would like VAL REF for another purpose. To be able to do: TYPE T = VAL OBJECT ... END; VAR aMegOfObjects := NEW(REF ARRAY OF T, 1024 * 1024); I think that is at least as useful as stack-allocated objects. What would be the meaning of TYPE T = VAL BRANDED OBJECT ... T = VAL UNTRACED REF ... At least the former is important because many existing OBJECT types are BRANDED. Yeah this won't work because the methods are messed up. See: TYPE T = OBJECT METHODS hello() := HelloT; END; PROCEDURE HelloT(self : T) = BEGIN IO.Put("hi!\n") END HelloT; now... TYPE U = VAL T; VAR u : U; BEGIN u.hello() (* what happens here? *) END the problem being that HelloT has the wrong type of initial argument. It needs to be of type VAL T, not T. What if you let it be of type T? Then consider... TYPE T = OBJECT METHODS goodbye() := GoodbyeT END; VAR trap : T; PROCEDURE GoodbyeT(me : T) = BEGIN trap := me END GoodbyeT; TYPE U = VAL T; BEGIN VAR u : U; BEGIN u.goodbye() END; (* u is out of scope here *) trap.goodbye() (* ????? *) END The declaration of U parenthesizes inconveniently. TYPE U = (VAL OBJECT METHODS goodbye() END) OVERRIDES goodbye := GoodbyeU END; If you don't override a method when you make a VAL object it must be overridden with NIL, to preclude making references to the stack. I guess you get a separate type hierarchy for VAL OBJECT types from OBJECT types, you can't inherit the methods. Now there is a school of thought in OOP, I know, that holds that inheriting methods is a Bad Idea and dangerous. That school would probably be OK with your idea. It says that it's OK, and a Good Thing, to inherit the method signature, since it is part of the specification. But implementations (i.e. method text) should not be inherited. Are you part of this school? Is there a problem with REF VAL OBJECT? Yeah it becomes ambiguous. Drat... is it (REF VAL) OBJECT = OBJECT or is it (REF (VAL OBJECT)), a REF to a given VAL OBJECT type? These are obviously different concepts. Are you sure you want this? It seems tricky and not that convenient after all. Mika On Fri, May 7, 2021 at 11:53 PM Jay K > wrote: Here are some language features I would like. 1. OBJECTs that are not heap allocated. Given that records can be on the stack or global, and can contain function pointers, why must OBJECTS be heap allocated? The syntax is almost obvious, except that I think OBJECT implies REF. Perhaps we could say "VALUE" to negate that? Or the existing keyword VAL could be repurposed? 2. non-virtual object methods; or merely non-virtual member functions in records. TYPE Person = RECORD birth_year: INTEGER; CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) END; PROCEDURE Person_Age(VAR self: Person): INTEGER = BEGIN RETURN Date.CurrentYear() - self.birth_year; END Person_Age; VAR johnDoe := Person{1980}; IO.Put(Fmt.Int(johnDoe.age()); => equivalent to Person_Age(johnDoe), but using the type of the prefix parameter to disambiguate the short name "age", so I do not have to state the type in the function name over and over. ? This could kinda be considered the same request..if you give records the ability to inherit..you know, how in C++, class and struct are really the same thing. I get that Modula-3 is optionally safe, has no preprocessor, has easier to implement generics -- it is not C++ -- but are these bad features? - Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From hendrik at topoi.pooq.com Mon May 10 03:17:16 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Sun, 9 May 2021 21:17:16 -0400 Subject: [M3devel] language evolution? In-Reply-To: References: Message-ID: <20210510011716.jntgglhuycw3l57c@topoi.pooq.com> On Mon, May 10, 2021 at 12:38:26AM +0000, Jay K wrote: ... > > Go is also wierd/interesting/different?inspiring in that stack/heap allocation really is determined by escape analysis. I was surprised to see that. It's probably the simplest unobtrusive safe way that leaves you flexibility and safety. Algol 68 would have been simpler if they had designed it to do that. There was a proposal that could have been implemented that way. But maybe harder to implement if the implementor needed to avoid just putting the entire stack on the heap. -- hendrik From jayk123 at hotmail.com Tue May 11 06:48:26 2021 From: jayk123 at hotmail.com (Jay K) Date: Tue, 11 May 2021 04:48:26 +0000 Subject: [M3devel] M3ID marks? Message-ID: I was look at M3ID.m3. This "mark" stuff, maybe is not the best tradeoff? I mean, if you expand the marks to INTEGER or LONGINT, certainly LONGINT, then they will never rollover and the result of SetMark will be reliable and callers will not have to double check? It will be easier to understand? At the cost of 8 bytes instead of 1 byte per id. Maybe that would be a worthwhile change? I know, don't "fix" what isn't "broken". (LONGINT cannot rollover, if occasionally incremented by one, because computers are not nearly that fast. If they were that fast, these numbers correlate with number of scopes in a single module I think, which fit in memory.) The code could be even "simpler". It could just single bit booleans, if fully cleared for every check (AdvanceMark). But perhaps touching every element for the clear would be too slow? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Tue May 11 18:20:47 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Tue, 11 May 2021 11:20:47 -0500 Subject: [M3devel] language evolution? In-Reply-To: References: Message-ID: <9b202769-7e8d-d48e-2d91-40bb12084195@lcwb.coop> On 5/8/21 1:52 AM, Jay K wrote: > Here are some language features I would like. > > 1. OBJECTs that are not heap allocated. > > Given that records can be on the stack or global, and can contain function pointers, > why must OBJECTS be heap allocated? Because variables of the object type can dynamically be of any subtype, and therefore dynamically any size. Moreover, the set of possible subtypes and therefore sizes is unknown at the point of variable declaration. Allowing this creates just one of the tangled webs of semantics that cause a language to be 300 pages rather than 50, which very few working programmers understand anyway. Especially as it interacts with the other tangled webs. > > The syntax is almost obvious, except that I think OBJECT implies REF. > Perhaps we could say "VALUE" to negate that? > Or the existing keyword VAL could be repurposed? > > 2. non-virtual object methods; or merely non-virtual member functions in records. Another tangled web. It confuses two semantically different constructs and tries to cram them into one procrustian bed. You get semantically very different things that sometimes, but not always, look syntactically the same, but aren't. Also semantically redundant things with different semantics but no differences in what they can accomplish and that sometimes look the same, sometimes not. For details, see https://queue.acm.org/detail.cfm?id=1028906 Ignore the first paragraph. Its a mistake. This is how a language gets 6 times as complicated with less real power. > > TYPE Person = RECORD > ? ?birth_year: INTEGER; > ? ?CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) > ?END; > > > PROCEDURE Person_Age(VAR self: Person): INTEGER = > BEGIN > ? RETURN Date.CurrentYear() - self.birth_year; > END Person_Age; > > VAR johnDoe := Person{1980}; > > IO.Put(Fmt.Int(johnDoe.age()); > > ?=> equivalent to Person_Age(johnDoe), but using the type > ? ? of the prefix parameter to disambiguate the short name "age", > ? ? so I do not have to state the type in the function name over and over. > > ? > > This could kinda be considered the same request..if you give records the ability > to inherit..you know, how in C++, class and struct are really the same thing. > > I get that Modula-3 is optionally safe, has no preprocessor, has easier > to implement generics -- it is not C++ -- but are these bad features? > > ?- Jay > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Tue May 11 22:23:08 2021 From: jayk123 at hotmail.com (Jay K) Date: Tue, 11 May 2021 20:23:08 +0000 Subject: [M3devel] how to build the compiler/libraries In-Reply-To: References: Message-ID: Have you tried boot2.py? Specifically, have cm3 in path, and boot2.py? I am working on a "big" change to boot1.py. And then maybe we can get CI. Or yes make-dist.py should work. What is the error? make-dist.py might require m3core and libm3. boot2.py only requires cm3 (and mklib if on Windows, but this will hopefully change). I never use any of the .sh files, unless they are in the python directory and thin wrappers over python. i.e. the process to cross build, same as process to "move" to another machine, is boot1.py and then boot2.py..which the untar + make + cp in between. I have written this all down a few times but to nobody's satisfaction I think. - Jay ________________________________ From: Alexey Ershov Sent: Tuesday, May 11, 2021 8:18 PM To: jayk123 at hotmail.com Cc: alexey.w.ershov at gmail.com Subject: how to build the compiler/libraries Hi Jay. Sorry for the bother. I'm searching for somebody who knows is there any 'standard' way to build the whole cm3 compiler/libraries/pkg set from latest sources taken from github. I already tried to use scripts/python/make-dist.py and scripts/do-cm3-std.sh and build process has failed (BTW in June 2020 I successfully built with make-dist.py). Perhaps, now I did something wrong. Would you be so kind to teach me the whole build process - as if we build the next 'official' release? Or - is there any written guide to read and learn for myself? Thanks and regards -AlexeyE -------------- next part -------------- An HTML attachment was scrubbed... URL: From alexey.w.ershov at gmail.com Tue May 11 23:00:54 2021 From: alexey.w.ershov at gmail.com (Alexey Ershov) Date: Wed, 12 May 2021 00:00:54 +0300 Subject: [M3devel] how to build the compiler/libraries In-Reply-To: References: Message-ID: I work under Linux Mint 20 (MATE). $ git clone https://github.com/modula3/cm3.git cm3.build $ cd cm3.build/scripts/python $ export CM3=/usr/local/cm3.d5.11.0/bin/cm3 $ ./make-dist.py 2>&1 | tee ./_make-dist.py.log ... ?-> archiving libsysutils.a /usr/bin/ld.gold: error: cannot open /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so: No such file or directory /usr/bin/ld.gold: error: cannot open /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so: No such file or directory collect2: error: ld returned 1 exit status ? make_lib => 1 librarian failed building: sysutils Fatal Error: package build failed ... == package /home/alex/work/m3/cm3.build/m3-libs/sysutils == ?+++ /tmp/tmph7yyn3/compiler_with_previous/bin/cm3 -DBUILD_DIR=AMD64_LINUX?? -build -DROOT=/home/alex/work/m3/cm3.build -DTARGET=AMD64_LINUX +++ ?*** execution of [, ] failed *** The log is attached. Note that the libraries are actually exist $ ls -l /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so lrwxrwxrwx 1 alex alex 23 May 11 23:51 /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so -> ../../../lib/libm3.so.5 $ ls -l /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so lrwxrwxrwx 1 alex alex 27 May 11 23:51 /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so -> ../../../lib/libm3core.so.5 What I did wrong? On 5/11/21 11:23 PM, Jay K wrote: > Have you tried boot2.py? > > Specifically, have cm3 in path, and boot2.py? > > I am working on a "big" change to boot1.py. And then maybe we can get CI. > > Or yes make-dist.py should work. What is the error? > make-dist.py might require m3core and libm3. > boot2.py only requires cm3 (and mklib if on Windows, but this will > hopefully change). > I never use any of the .sh files, unless they are in the python > directory and thin wrappers over python. > > i.e. the process to cross build, same as process to "move" to another > machine, is boot1.py and then boot2.py..which the untar + make + cp in > between. > > I have written this all down a few times but to nobody's satisfaction > I think. > > ?- Jay > > ------------------------------------------------------------------------ > *From:* Alexey Ershov > *Sent:* Tuesday, May 11, 2021 8:18 PM > *To:* jayk123 at hotmail.com > *Cc:* alexey.w.ershov at gmail.com > *Subject:* how to build the compiler/libraries > Hi Jay. > > Sorry for the bother. I'm searching for somebody who knows is there any > 'standard' way to build the whole cm3 compiler/libraries/pkg set from > latest sources taken from github. I already tried to use > scripts/python/make-dist.py and scripts/do-cm3-std.sh and build process > has failed (BTW in June 2020 I successfully built with make-dist.py). > Perhaps, now I did something wrong. Would you be so kind to teach me the > whole build process - as if we build the next 'official' release? Or - > is there any written guide to read and learn for myself? > > Thanks and regards > -AlexeyE > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: _make-dist.py.log.tar.gz Type: application/gzip Size: 2470 bytes Desc: not available URL: From jayk123 at hotmail.com Wed May 12 00:08:59 2021 From: jayk123 at hotmail.com (Jay K) Date: Tue, 11 May 2021 22:08:59 +0000 Subject: [M3devel] how to build the compiler/libraries In-Reply-To: References: , Message-ID: That is what I said? make-dist.py might require m3core/libm3. Do the targets of the symlinks exist? I think we should stop using that structure, really, but it is topic of a not yet existing thread. boot2.py does not depend on m3core/libm3. Try it? - Jay ________________________________ From: Alexey Ershov Sent: Tuesday, May 11, 2021 9:00 PM To: Jay K Cc: m3devel Subject: Re: how to build the compiler/libraries I work under Linux Mint 20 (MATE). $ git clone https://github.com/modula3/cm3.git cm3.build $ cd cm3.build/scripts/python $ export CM3=/usr/local/cm3.d5.11.0/bin/cm3 $ ./make-dist.py 2>&1 | tee ./_make-dist.py.log ... -> archiving libsysutils.a /usr/bin/ld.gold: error: cannot open /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so: No such file or directory /usr/bin/ld.gold: error: cannot open /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so: No such file or directory collect2: error: ld returned 1 exit status make_lib => 1 librarian failed building: sysutils Fatal Error: package build failed ... == package /home/alex/work/m3/cm3.build/m3-libs/sysutils == +++ /tmp/tmph7yyn3/compiler_with_previous/bin/cm3 -DBUILD_DIR=AMD64_LINUX -build -DROOT=/home/alex/work/m3/cm3.build -DTARGET=AMD64_LINUX +++ *** execution of [, ] failed *** The log is attached. Note that the libraries are actually exist $ ls -l /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so lrwxrwxrwx 1 alex alex 23 May 11 23:51 /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so -> ../../../lib/libm3.so.5 $ ls -l /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so lrwxrwxrwx 1 alex alex 27 May 11 23:51 /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so -> ../../../lib/libm3core.so.5 What I did wrong? On 5/11/21 11:23 PM, Jay K wrote: Have you tried boot2.py? Specifically, have cm3 in path, and boot2.py? I am working on a "big" change to boot1.py. And then maybe we can get CI. Or yes make-dist.py should work. What is the error? make-dist.py might require m3core and libm3. boot2.py only requires cm3 (and mklib if on Windows, but this will hopefully change). I never use any of the .sh files, unless they are in the python directory and thin wrappers over python. i.e. the process to cross build, same as process to "move" to another machine, is boot1.py and then boot2.py..which the untar + make + cp in between. I have written this all down a few times but to nobody's satisfaction I think. - Jay ________________________________ From: Alexey Ershov Sent: Tuesday, May 11, 2021 8:18 PM To: jayk123 at hotmail.com Cc: alexey.w.ershov at gmail.com Subject: how to build the compiler/libraries Hi Jay. Sorry for the bother. I'm searching for somebody who knows is there any 'standard' way to build the whole cm3 compiler/libraries/pkg set from latest sources taken from github. I already tried to use scripts/python/make-dist.py and scripts/do-cm3-std.sh and build process has failed (BTW in June 2020 I successfully built with make-dist.py). Perhaps, now I did something wrong. Would you be so kind to teach me the whole build process - as if we build the next 'official' release? Or - is there any written guide to read and learn for myself? Thanks and regards -AlexeyE -------------- next part -------------- An HTML attachment was scrubbed... URL: From alexey.w.ershov at gmail.com Wed May 12 00:26:46 2021 From: alexey.w.ershov at gmail.com (Alexey Ershov) Date: Wed, 12 May 2021 01:26:46 +0300 Subject: [M3devel] how to build the compiler/libraries In-Reply-To: References: Message-ID: The targets are not exist. From where can they be borrowed? Who should put it on place? Does each new script run create a new temporary dir inside the 'tmp' ? boot2.py: failed (see attached log). I used d5.11.0 compiler from github at /usr/local/cm3 On 5/12/21 1:08 AM, Jay K wrote: > That is what I said? make-dist.py might require m3core/libm3. > Do the targets of the symlinks exist? > I think we should stop using that structure, really, but it is topic > of a not yet existing thread. > > boot2.py does not depend on m3core/libm3. Try it? > > ?- Jay > > ------------------------------------------------------------------------ > *From:* Alexey Ershov > *Sent:* Tuesday, May 11, 2021 9:00 PM > *To:* Jay K > *Cc:* m3devel > *Subject:* Re: how to build the compiler/libraries > > I work under Linux Mint 20 (MATE). > > > $ git clone https://github.com/modula3/cm3.git > > cm3.build > > $ cd cm3.build/scripts/python > $ export CM3=/usr/local/cm3.d5.11.0/bin/cm3 > $ ./make-dist.py 2>&1 | tee ./_make-dist.py.log > ... > > ?-> archiving libsysutils.a > /usr/bin/ld.gold: error: cannot open > /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so: > No such file or directory > /usr/bin/ld.gold: error: cannot open > /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so: > No such file or directory > collect2: error: ld returned 1 exit status > ? make_lib => 1 > librarian failed building: sysutils > Fatal Error: package build failed > ... > > == package /home/alex/work/m3/cm3.build/m3-libs/sysutils == > > ?+++ /tmp/tmph7yyn3/compiler_with_previous/bin/cm3 > -DBUILD_DIR=AMD64_LINUX?? -build -DROOT=/home/alex/work/m3/cm3.build > -DTARGET=AMD64_LINUX +++ > ?*** execution of [, > ] failed *** > > > The log is attached. Note that the libraries are actually exist > > > $ ls -l > /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so > lrwxrwxrwx 1 alex alex 23 May 11 23:51 > /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so > -> ../../../lib/libm3.so.5 > $ ls -l > /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so > lrwxrwxrwx 1 alex alex 27 May 11 23:51 > /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so > -> ../../../lib/libm3core.so.5 > > > What I did wrong? > > > On 5/11/21 11:23 PM, Jay K wrote: >> Have you tried boot2.py? >> >> Specifically, have cm3 in path, and boot2.py? >> >> I am working on a "big" change to boot1.py. And then maybe we can get CI. >> >> Or yes make-dist.py should work. What is the error? >> make-dist.py might require m3core and libm3. >> boot2.py only requires cm3 (and mklib if on Windows, but this will >> hopefully change). >> I never use any of the .sh files, unless they are in the python >> directory and thin wrappers over python. >> >> i.e. the process to cross build, same as process to "move" to another >> machine, is boot1.py and then boot2.py..which the untar + make + cp >> in between. >> >> I have written this all down a few times but to nobody's satisfaction >> I think. >> >> ?- Jay >> >> ------------------------------------------------------------------------ >> *From:* Alexey Ershov >> >> *Sent:* Tuesday, May 11, 2021 8:18 PM >> *To:* jayk123 at hotmail.com >> >> *Cc:* alexey.w.ershov at gmail.com >> >> *Subject:* how to build the compiler/libraries >> Hi Jay. >> >> Sorry for the bother. I'm searching for somebody who knows is there any >> 'standard' way to build the whole cm3 compiler/libraries/pkg set from >> latest sources taken from github. I already tried to use >> scripts/python/make-dist.py and scripts/do-cm3-std.sh and build process >> has failed (BTW in June 2020 I successfully built with make-dist.py). >> Perhaps, now I did something wrong. Would you be so kind to teach me the >> whole build process - as if we build the next 'official' release? Or - >> is there any written guide to read and learn for myself? >> >> Thanks and regards >> -AlexeyE >> -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: _boot2.py.log.tar.gz Type: application/gzip Size: 121044 bytes Desc: not available URL: From jayk123 at hotmail.com Wed May 12 01:41:49 2021 From: jayk123 at hotmail.com (Jay K) Date: Tue, 11 May 2021 23:41:49 +0000 Subject: [M3devel] how to build the compiler/libraries In-Reply-To: References: , Message-ID: The error is: > "/home/alex/work/m3/cm3.build/m3-db/db/src/mysqldb/m3makefile", line 2: > quake runtime error: unable to open "/usr/local/cm3/pkg/mysql/AMD64_LINUX/.M3EXPORTS" for reading This is a long standing problem in the cm3 build system. We don't have a good way to automatically detect dependencies and either tell user how to get them, or skip what depends on them. Remove mysqldb from pkginfo.txt, or apt-get install mysql something.. > make-dist + m3core/libm3 > can m3core/libm3 be borrowed from somewhere Not really. The present "thinking" in make-dist.py is you have cm3+m3core+libm3. But yeah it could/should be made more like boot2. Possibly I copied the idea of some .sh files or possibly I misunderstood. I think I misunderstood -- we should default to building m3core/libm3, and only fallback to preexisting, if the first fails and they exist. I think I sent mail suggesting that recently. This will usually work and feels more natural to more people, and also fixes some scenarios. It only does not work on the rare occasion when the language and libraries changed together, such that existing cm3 cannot compile current m3core/libm3, such as when LONGINT was added. I misread upgrade.sh at some point, as I think it already takes that approach, but upgrade.py and make-dist.py do not. But if you boot2.py first, then make-dist.py will work. - Jay ________________________________ From: Alexey Ershov Sent: Tuesday, May 11, 2021 10:26 PM To: Jay K Cc: m3devel Subject: Re: how to build the compiler/libraries The targets are not exist. From where can they be borrowed? Who should put it on place? Does each new script run create a new temporary dir inside the 'tmp' ? boot2.py: failed (see attached log). I used d5.11.0 compiler from github at /usr/local/cm3 On 5/12/21 1:08 AM, Jay K wrote: That is what I said? make-dist.py might require m3core/libm3. Do the targets of the symlinks exist? I think we should stop using that structure, really, but it is topic of a not yet existing thread. boot2.py does not depend on m3core/libm3. Try it? - Jay ________________________________ From: Alexey Ershov Sent: Tuesday, May 11, 2021 9:00 PM To: Jay K Cc: m3devel Subject: Re: how to build the compiler/libraries I work under Linux Mint 20 (MATE). $ git clone https://github.com/modula3/cm3.git cm3.build $ cd cm3.build/scripts/python $ export CM3=/usr/local/cm3.d5.11.0/bin/cm3 $ ./make-dist.py 2>&1 | tee ./_make-dist.py.log ... -> archiving libsysutils.a /usr/bin/ld.gold: error: cannot open /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so: No such file or directory /usr/bin/ld.gold: error: cannot open /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so: No such file or directory collect2: error: ld returned 1 exit status make_lib => 1 librarian failed building: sysutils Fatal Error: package build failed ... == package /home/alex/work/m3/cm3.build/m3-libs/sysutils == +++ /tmp/tmph7yyn3/compiler_with_previous/bin/cm3 -DBUILD_DIR=AMD64_LINUX -build -DROOT=/home/alex/work/m3/cm3.build -DTARGET=AMD64_LINUX +++ *** execution of [, ] failed *** The log is attached. Note that the libraries are actually exist $ ls -l /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so lrwxrwxrwx 1 alex alex 23 May 11 23:51 /tmp/tmph7yyn3/compiler_with_previous/pkg/libm3/AMD64_LINUX/libm3.so -> ../../../lib/libm3.so.5 $ ls -l /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so lrwxrwxrwx 1 alex alex 27 May 11 23:51 /tmp/tmph7yyn3/compiler_with_previous/pkg/m3core/AMD64_LINUX/libm3core.so -> ../../../lib/libm3core.so.5 What I did wrong? On 5/11/21 11:23 PM, Jay K wrote: Have you tried boot2.py? Specifically, have cm3 in path, and boot2.py? I am working on a "big" change to boot1.py. And then maybe we can get CI. Or yes make-dist.py should work. What is the error? make-dist.py might require m3core and libm3. boot2.py only requires cm3 (and mklib if on Windows, but this will hopefully change). I never use any of the .sh files, unless they are in the python directory and thin wrappers over python. i.e. the process to cross build, same as process to "move" to another machine, is boot1.py and then boot2.py..which the untar + make + cp in between. I have written this all down a few times but to nobody's satisfaction I think. - Jay ________________________________ From: Alexey Ershov Sent: Tuesday, May 11, 2021 8:18 PM To: jayk123 at hotmail.com Cc: alexey.w.ershov at gmail.com Subject: how to build the compiler/libraries Hi Jay. Sorry for the bother. I'm searching for somebody who knows is there any 'standard' way to build the whole cm3 compiler/libraries/pkg set from latest sources taken from github. I already tried to use scripts/python/make-dist.py and scripts/do-cm3-std.sh and build process has failed (BTW in June 2020 I successfully built with make-dist.py). Perhaps, now I did something wrong. Would you be so kind to teach me the whole build process - as if we build the next 'official' release? Or - is there any written guide to read and learn for myself? Thanks and regards -AlexeyE -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Wed May 12 07:49:10 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 12 May 2021 05:49:10 +0000 Subject: [M3devel] new compiler dependency: set Message-ID: heads up: I want to use the existing library "set" in cm3. This isn't too earth shattering, but anyone not using the scripts might get a broken workflow. I could probably avoid it by making another instantiation with another name but I don't think we should do that. The system works ok without that and it is just wasteful. Alternatively, using the bitvector, another library we already have, make some sense, but I think less. I am not changing the set library at all, just using it (any old build will work). If there is some reason not to, I could get by with table, mapping to unused data. Kinda yucky but ok. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 13 08:54:34 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 13 May 2021 06:54:34 +0000 Subject: [M3devel] representation of typenames: qid, m3id, other? Message-ID: Hi I have a bit of a recurring dilema. In m3front/m3middle/m3back, how to represent a "typename". In source they take two forms: X Module.X In various data structures, in m3front, there is prior art that this is a QID, an optionally module qualified id, a pair of M3ID.T, a pair of integers. I have been "extending" this -- sticking with qid and using them more. This is ok, but what I keep coming back to is wanting, maybe, like: 1. Well, first I already started mutating the "original" QID. I fill in module. That said, they can also be "INTEGER", module stays empty or 0. But imho this is good, we no longer have user defined X, just Module.X and some builtin plain X. 2. And then, it is tempting, roughly: typename: M3ID.T := M3ID.Add(M3ID.ToText(qid.module) & "__" & M3ID.ToText(qid.item)); of course if module = 0 then just return qid.item, and use GlobalName to do the concat. i.e. for builtin types. I think the qid type is a little unwieldy and not much used, I'd rather just use M3ID.T. However, many backends ignore these values and forming them is wasteful. That is the main reason why not. The C backend would use them directly, doing the concat shown here. Also maybe maybe there are other ways other code would want to do the combining. For example using a dot. For example maybe a debugger. Or hey some future C++ backend might want Module::X. Really, I doubt that, it isn't worth it. Or maybe some future friendly C++ binding, but still, I doubt it. (In particular "friendly C++ binding with garbage collection" is trouble, though bindings involving untraced types, sure) I think for now I'll stick with qid, to minimize impact on other backends, but if there is consensus, I can change it. I think i will start calling the variable names typename though, rather than just qid or typeqid or typenameqid, etc. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 13 09:25:09 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 13 May 2021 07:25:09 +0000 Subject: [M3devel] language evolution? In-Reply-To: <9b202769-7e8d-d48e-2d91-40bb12084195@lcwb.coop> References: , <9b202769-7e8d-d48e-2d91-40bb12084195@lcwb.coop> Message-ID: > Because variables of the object type can dynamically be of any > subtype, and therefore dynamically any size. Moreover, the > set of possible subtypes and therefore sizes is unknown at > the point of variable declaration. This doesn't really matter. True that "objects" would rarely be passed by value. But VAR is ok. You know, it works fine and simply in C++. > Allowing this creates just one of the tangled webs of semantics I think this is missing some things. I might write: INTERFACE M; TYPE T; M.Function(t:T); and then it is an implementation detail what M.Function does with t. - It might ignore it entirely. In present code. - It might only turn around and call t.function - It might do stuff in addition to calling t.function. Or I might have a client direction call: t.function() and still function might not use the first parameter, using it only for dynamic dispatch. And as implementation details change, maybe the first parameter goes in and out of use. For maximum flexibility of an evolving implementation, M.Function(t) is "best". Of course, I cannot pass all state as parameters to all functions "just in case". But maybe one special reserved one? But writing the "M." part is tedious. Furthermore, the caller might be evolving. Maybe the caller changes his code and t changes from M.T to M2.T. It is really nice to just change the declaration and/or initialization, but not have to visit every call. (and furthermore use static type inference, or templates-in-depth, so the static type is not repeated so many times). In either case, it is subjective. What you call "messy" is also useful, and perhaps not an accident or thoughtless. Repeating self, I could have C++: class A { static void f1(); void f2(); virtual void f3(); }; A a; a.f1(); a.f2(); a.f3(); Under maintenance, static and virtual can come and go, but the caller can stay the same. The caller does not necessarily care which is static or virtual. He trusts the implementer of A. Just as he trusts the implementer of A to fill in the bodies of the functions. Resolving function calls based on static or dynamic type of a parameter is useful separately from whether or not the lifetime of the parameters is known/LIFO or unknown/GC. In fact, most languages are kinda limited here. As I recall, Dylan allows the dynamic types of multiple parameters to determine dispatch. But the obvious efficient implementation strategy breaks down. The thing about classes full of static functions resembling namespaces is widely acknowledged. Classes came long before namespaces. Namespaces are slightly better because you can open them multiple times, spreading out the declarations. There are tradeoffs though, in so many things. Plain text search is a very strong opposing force to all shorthands, object.method, etc. 'cause then "method" is ambiguous and "object." does not "easily" qualify it (from a plain text point single line of view; it does well for the compiler). Most people ignore this though and object.method is rampant. Likewise I cannot search for all uses of type.data, unless it is type.typedata, or accessed through a function Type.Data. > You get semantically very different things This is assuming that the semantic of function call resolution are significant to the caller, vs. an abstracted away and changable detail of the set of target functions. They really might not be. > less real power. There is some difficult/impossible to answer thing about "minor syntactic sugar" vs. "worthwhile syntactic sugar". For example, + is generic/overloaded in C and Modula-3. It works on multiple integer and floating point types. Is that significant? Should infix operators be: - Only available for special cased builtins ? - Available for user defined types ? - Removed entirely as not-general, in the absence of previous? Should we write IntAdd, LongAdd, FloatAdd? Simlarly the LOCK construct is redundant with TRY/FINALLY. Why is it worth providing? Why cannot I not do similar with user defined types (reader/writer lock?), a kind of one line try/finally? (Or even Go defer where the finally is adjacent to the try for easier maintenance ("put related things near each other, possibly contradicting execution order")). We must a find a balance. We must have a language that is neither near-zero size nor near-infinite size. - Jay ________________________________ From: Rodney M. Bates Sent: Tuesday, May 11, 2021 4:20 PM To: Jay K ; m3devel Subject: Re: [M3devel] language evolution? On 5/8/21 1:52 AM, Jay K wrote: > Here are some language features I would like. > > 1. OBJECTs that are not heap allocated. > > Given that records can be on the stack or global, and can contain function pointers, > why must OBJECTS be heap allocated? Because variables of the object type can dynamically be of any subtype, and therefore dynamically any size. Moreover, the set of possible subtypes and therefore sizes is unknown at the point of variable declaration. Allowing this creates just one of the tangled webs of semantics that cause a language to be 300 pages rather than 50, which very few working programmers understand anyway. Especially as it interacts with the other tangled webs. > > The syntax is almost obvious, except that I think OBJECT implies REF. > Perhaps we could say "VALUE" to negate that? > Or the existing keyword VAL could be repurposed? > > 2. non-virtual object methods; or merely non-virtual member functions in records. Another tangled web. It confuses two semantically different constructs and tries to cram them into one procrustian bed. You get semantically very different things that sometimes, but not always, look syntactically the same, but aren't. Also semantically redundant things with different semantics but no differences in what they can accomplish and that sometimes look the same, sometimes not. For details, see https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fqueue.acm.org%2Fdetail.cfm%3Fid%3D1028906&data=04%7C01%7C%7C0e4c507418394c42f26c08d91498c5aa%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637563468649071906%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=AnHQYrRBs4ZvQFZ0wzkzJ7DDZRT2kcBJXHxgaZR3HEM%3D&reserved=0 Ignore the first paragraph. Its a mistake. This is how a language gets 6 times as complicated with less real power. > > TYPE Person = RECORD > birth_year: INTEGER; > CONST PROCEDURE age():INTEGER = Person_Age; (* not a function pointer *) > END; > > > PROCEDURE Person_Age(VAR self: Person): INTEGER = > BEGIN > RETURN Date.CurrentYear() - self.birth_year; > END Person_Age; > > VAR johnDoe := Person{1980}; > > IO.Put(Fmt.Int(johnDoe.age()); > > => equivalent to Person_Age(johnDoe), but using the type > of the prefix parameter to disambiguate the short name "age", > so I do not have to state the type in the function name over and over. > > ? > > This could kinda be considered the same request..if you give records the ability > to inherit..you know, how in C++, class and struct are really the same thing. > > I get that Modula-3 is optionally safe, has no preprocessor, has easier > to implement generics -- it is not C++ -- but are these bad features? > > - Jay > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C0e4c507418394c42f26c08d91498c5aa%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637563468649081902%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=4kSiJzALE%2BFDSINHXCP2S0E8qyIfVM%2BkhteRpSAk9AE%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Tue May 18 22:41:30 2021 From: jayk123 at hotmail.com (Jay K) Date: Tue, 18 May 2021 20:41:30 +0000 Subject: [M3devel] declare_param typenames In-Reply-To: References: <20210504090628.FAB8D5DC@m0117565.ppops.net>, Message-ID: Fyi, This turns out to be a lot more than I realized. But I have been able to make progress. The two recurring patterns are: - Many places a typeuid is passed to the backend, also pass a QID. Arguably there should just be one parameter, QID or M3ID, but this is far less "explosive". Everything keeps working easily. I haven't done this everywhere needed yet. It is conceivable we should be using strings much more and hashes much less. - Many places in m3front looks like: self.type : = something self.type := something2(self.type) change to: self.type := something self.original_type := type self.type := something2(self.type); get self.original_type typename though possibly would work: self.type := something EVAL something2(self.type); get self.type typename I didn't want the risk of performance or semantic change there, at least not so thoroughly. There are places where I return original_type though, like where Formals are split and recreated (seems messy, I should look more closely..) - Jay ________________________________ From: Jay K Sent: Tuesday, May 4, 2021 9:36 PM To: rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames I am not sure if what I want to do will help that. Whenever I get those errors I just rebuild from m3core and up. I appreciate that the checking exists, at least. It was probably the best system by far at the time it was written, and still competitive. I do see similar errors from C++ LTCG/LTO about multiple sizes for same type, and the type names are given, and C++ users and providers do want to provide ODR violation errors, but it is a very long time coming. I learned to not hold my breath. - Jay ________________________________ From: Rodney Bates Sent: Tuesday, May 4, 2021 4:06 PM To: Jay K ; rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames -Rodney Bates --- jayk123 at hotmail.com wrote: From: Jay K To: m3devel Subject: [M3devel] declare_param typenames Date: Tue, 27 Apr 2021 17:01:44 +0000 Fyi declare_param lacks typenames. It only has typeid. So it is a bit lossy. C backend could use better fidelity here. I intend to add a M3.QID parameter. The reader/writer can ignore it, so m3cc completely unchanged. M3x86 and M3Llvm can also ignore it, requiring a few lines. - Jay I am in favor this and similar changes. While our compilation system is quite good at handling separate unit compilation and recompilation within a package, the inter-package stuff emits nearly unusable error messages for inconsistencies, missing types, etc. All they give is a UID for problem types, and not even in the same base as elsewhere. Diagnosing such problems is a mix of sheer guesswork and dumb luck. Mercifully, they don't happen real often, at least not to me. I have toyed with improving the messages in the past, but it first entails putting type names in a number of the IR operators. Each such change has to be made in somewhere around 10 different places, so it hasn't made it to the top of my priority list. _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cd05c0073508746786ff008d90f169832%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637557411976234635%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=%2Fm7ET0tgq6rwrr1Kr7j32l5c7NpBD7FUP95JHXNyzz4%3D&reserved=0 -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Wed May 19 01:41:28 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Tue, 18 May 2021 18:41:28 -0500 Subject: [M3devel] declare_param typenames In-Reply-To: References: <20210504090628.FAB8D5DC@m0117565.ppops.net> Message-ID: <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop> On 5/18/21 3:41 PM, Jay K wrote: > Fyi, This turns out to be a lot more than I realized. > But I have been able to make progress. > > The two recurring patterns are: > > ?- Many places a typeuid is passed to the backend, also pass a QID. > ? ?Arguably there should just be one parameter, QID or M3ID, but this is far less "explosive". > ? ?Everything keeps working easily. > ? I haven't done this everywhere needed yet. > > It is conceivable we should be using strings much more and hashes much less. Strings can only be used as a convenience for humans, through debuggers, etc. The language is very clear that a type is defined structurally, not by its name. There can be multiple occurrences of the same type expression, with several names and some anonymous. They are all the same type. The semantic processing has to use the hashes or equivalent to properly implement the language. The one exception is a BRANDED type with no brand string, but even here, this is explained as the language supplying an implicit brand string, not used in any other brand, so by that view, even this case is structural. > > ?- Many places in m3front looks like: > ?self.type : = something > self.type := something2(self.type) > > change to: > self.type := something > self.original_type := type > self.type := something2(self.type); > ? get self.original_type typename > > though possibly would work: > self.type := something > ?EVAL?something2(self.type); > ? get self.type typename > > I didn't want the risk of performance or semantic change there, at least not so thoroughly. > There are places where I return original_type though, like where Formals are split and recreated (seems messy, I should look more closely..) > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Jay K > *Sent:* Tuesday, May 4, 2021 9:36 PM > *To:* rodney.m.bates at acm.org > *Cc:* m3devel at elegosoft.com > *Subject:* Re: [M3devel] declare_param typenames > I am not sure if what I want to do will help that. > > Whenever I get those errors I just rebuild from m3core and up. > I appreciate that the checking exists, at least. > It was probably the best system by far at the time it was written, > and still competitive. > > I do see similar errors from C++ LTCG/LTO about multiple sizes for same type, > and the type names are given, and C++ users and providers do want > to provide ODR violation errors, but it is a very long time coming. > I learned to not hold my breath. > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Rodney Bates > *Sent:* Tuesday, May 4, 2021 4:06 PM > *To:* Jay K ; rodney.m.bates at acm.org > *Cc:* m3devel at elegosoft.com > *Subject:* Re: [M3devel] declare_param typenames > > > -Rodney Bates > > --- jayk123 at hotmail.com wrote: > > From: Jay K > To: m3devel > Subject: [M3devel] declare_param typenames > Date: Tue, 27 Apr 2021 17:01:44 +0000 > > Fyi declare_param lacks typenames. It only has typeid. So it is a bit lossy. C backend could use better fidelity here. I intend to add a M3.QID parameter. The reader/writer can ignore it, so m3cc completely unchanged. M3x86 and M3Llvm can also ignore it, requiring a few lines. > > - Jay > > I am in favor this and similar changes.? While our compilation system is quite > good at handling separate unit compilation and recompilation within a package, > the inter-package stuff emits nearly unusable error messages for inconsistencies, > missing types, etc. All they give is a UID for problem types, and not even in > the same base as elsewhere.? Diagnosing such problems is a mix of sheer guesswork > and dumb luck.? Mercifully, they don't happen real often, at least not to me. > > I have toyed with improving the messages in the past, but it first entails putting > type names in a number of the IR operators.? Each such change has to be made in somewhere > around 10 different places,? so it hasn't made it to the top of my priority list. > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cd05c0073508746786ff008d90f169832%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637557411976234635%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=%2Fm7ET0tgq6rwrr1Kr7j32l5c7NpBD7FUP95JHXNyzz4%3D&reserved=0 > > > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From rodney_bates at lcwb.coop Wed May 19 02:21:09 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Tue, 18 May 2021 19:21:09 -0500 Subject: [M3devel] g++9 and g++-8 undermine debugging Message-ID: g++9, from experimental evidence, is doing something to _m3main.o that gives m3gdb bad code addresses. m3gdb tries to insert breakpoints at invalid addresses and fails. This is for breakpoints in m3-compiled code, not in _m3main itself. g++-8 also fails. g++5 is what I have used previously, and it works. These are the only ones I have tried and can easily try. -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Wed May 19 02:31:55 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 19 May 2021 00:31:55 +0000 Subject: [M3devel] g++9 and g++-8 undermine debugging In-Reply-To: References: Message-ID: Can you elaborate on the matrix? gcc vs. g++ vs. clang vs. clang++ ? Do any of them work? -g vs. -gstabs vs. other forms of -g? Does anything work? "main in C" or not? Does anything work? I changed "it", I forgot, config or builder, so main is always in C, but I didn't remove the old code, so maybe try the other? Can gdb or lldb debug the C code? I realize that might not matter. I assume g++ -g/-gstabs/etc. on non-Modula3 hello world works for you? (well, some versions of -gstabs do have problems with C, but not what you describe.) - Jay ________________________________ From: M3devel on behalf of Rodney M. Bates Sent: Wednesday, May 19, 2021 12:21 AM To: m3devel Subject: [M3devel] g++9 and g++-8 undermine debugging g++9, from experimental evidence, is doing something to _m3main.o that gives m3gdb bad code addresses. m3gdb tries to insert breakpoints at invalid addresses and fails. This is for breakpoints in m3-compiled code, not in _m3main itself. g++-8 also fails. g++5 is what I have used previously, and it works. These are the only ones I have tried and can easily try. -- Rodney Bates rodney.m.bates at acm.org _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Ce1c5fa0e6c6b42ddc1d708d91a5c16d2%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637569805084025899%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=PlZtnSlKsikiWZg9bXvH%2FLCyrGYUiZfX27GDb4oBY9k%3D&reserved=0 -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Wed May 19 05:28:18 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 19 May 2021 03:28:18 +0000 Subject: [M3devel] declare_param typenames In-Reply-To: <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop> References: <20210504090628.FAB8D5DC@m0117565.ppops.net> , <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop> Message-ID: > The language is very clear that a type is defined structurally Does that matter to backends? Not clear. Presumably the integrated backend does nothing with types. I realize m3cc uses the uids a lot, at least for m3gdb purposes. I don't remember if otherwise. The C backend presently converts them to strings, and, you know, there is a vigorous amount of casting in the IR, so types probably don't need to match. Though I would like to improve that. Perhaps generating almost readable idiomatic C, but that is unlikely or far off. Is it the right design? Not clear. It appears it was very much debated and this didn't win by much. There are advantages and disadvantages either way, esp. depending on context. C++ for its part is a mix, and does not imho provide all the desired functionality. Nor does Modula-3 provide all the desired functionality. In particular I would like: TYPE inches = INTEGER; TYPE meters = INTEGER; to optionally produce unique types. C++ has a workaround: struct inches { int value; }; struct meters { int value; }; I think Modula-3 has similar workaround but requires indirection and therefore "preferably" GC? i.e. branded pointers. Or can we have branded records w/o indirection? I was going to say C++ is name-based but that is obviously false, because people frequently say anonymous types like T* or unique_ptr which requires structural equivalence. Every struct and I believe every enum does introduce a new type. Which is certainly usually useful. typedefs, like the inches/meters example, are transparent, which is sometimes desirable, sometimes not. Also the hashes collide. So we don't quite have structural equivalence, we have structural hash equivalence. :( Right? Or maybe m3front does a more complete check, and a type-less backend like M3x86 is ok with hash collisions. At least with names, or canonical arbitrary length "string" forms of structure, you get no collisions. This is easier with C++ since you can "stop" at struct/enum. Modula-3 has to encode more information usually. I realize, like the book details, there are advantages and disadvantages either way. Most code is ok either way. Some requires one, some hypothetically requires the other. Anyway, on a more practical note.. given m3cg.foo(Typeuid) that I extend to m3cg.foo(typeuid, typename:qid) I wonder if we should combine these into one small struct. What happens if we pass even more parameters? Or heck, maybe we should really: m3cg.foo(var:fooParams). It is a tradeoff. If new params are added that most backends must not ignore, then the first form is "much" better. If new params are added just for some backends, then the second form is "much" better. I do already have records defined to store all the cg call parameters. M3C heap allocates them all and makes multiple passes. It'd be an exceedingly tedious change, to arrive back in the same place, but might be more maintainable long term (nevermind that the present form has changed very little over the very long term ? ) - Jay ________________________________ From: Rodney M. Bates Sent: Tuesday, May 18, 2021 11:41 PM To: Jay K ; rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames On 5/18/21 3:41 PM, Jay K wrote: > Fyi, This turns out to be a lot more than I realized. > But I have been able to make progress. > > The two recurring patterns are: > > - Many places a typeuid is passed to the backend, also pass a QID. > Arguably there should just be one parameter, QID or M3ID, but this is far less "explosive". > Everything keeps working easily. > I haven't done this everywhere needed yet. > > It is conceivable we should be using strings much more and hashes much less. Strings can only be used as a convenience for humans, through debuggers, etc. The language is very clear that a type is defined structurally, not by its name. There can be multiple occurrences of the same type expression, with several names and some anonymous. They are all the same type. The semantic processing has to use the hashes or equivalent to properly implement the language. The one exception is a BRANDED type with no brand string, but even here, this is explained as the language supplying an implicit brand string, not used in any other brand, so by that view, even this case is structural. > > - Many places in m3front looks like: > self.type : = something > self.type := something2(self.type) > > change to: > self.type := something > self.original_type := type > self.type := something2(self.type); > get self.original_type typename > > though possibly would work: > self.type := something > EVAL something2(self.type); > get self.type typename > > I didn't want the risk of performance or semantic change there, at least not so thoroughly. > There are places where I return original_type though, like where Formals are split and recreated (seems messy, I should look more closely..) > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Jay K > *Sent:* Tuesday, May 4, 2021 9:36 PM > *To:* rodney.m.bates at acm.org > *Cc:* m3devel at elegosoft.com > *Subject:* Re: [M3devel] declare_param typenames > I am not sure if what I want to do will help that. > > Whenever I get those errors I just rebuild from m3core and up. > I appreciate that the checking exists, at least. > It was probably the best system by far at the time it was written, > and still competitive. > > I do see similar errors from C++ LTCG/LTO about multiple sizes for same type, > and the type names are given, and C++ users and providers do want > to provide ODR violation errors, but it is a very long time coming. > I learned to not hold my breath. > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Rodney Bates > *Sent:* Tuesday, May 4, 2021 4:06 PM > *To:* Jay K ; rodney.m.bates at acm.org > *Cc:* m3devel at elegosoft.com > *Subject:* Re: [M3devel] declare_param typenames > > > -Rodney Bates > > --- jayk123 at hotmail.com wrote: > > From: Jay K > To: m3devel > Subject: [M3devel] declare_param typenames > Date: Tue, 27 Apr 2021 17:01:44 +0000 > > Fyi declare_param lacks typenames. It only has typeid. So it is a bit lossy. C backend could use better fidelity here. I intend to add a M3.QID parameter. The reader/writer can ignore it, so m3cc completely unchanged. M3x86 and M3Llvm can also ignore it, requiring a few lines. > > - Jay > > I am in favor this and similar changes. While our compilation system is quite > good at handling separate unit compilation and recompilation within a package, > the inter-package stuff emits nearly unusable error messages for inconsistencies, > missing types, etc. All they give is a UID for problem types, and not even in > the same base as elsewhere. Diagnosing such problems is a mix of sheer guesswork > and dumb luck. Mercifully, they don't happen real often, at least not to me. > > I have toyed with improving the messages in the past, but it first entails putting > type names in a number of the IR operators. Each such change has to be made in somewhere > around 10 different places, so it hasn't made it to the top of my priority list. > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C5fe01fe2b4ab4a0be6a308d91a567fb5%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637569781081015823%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=nbTvitI61qO%2BLeD2t7XgTzvdnySYxdHuXSt36ejY7kY%3D&reserved=0 > > > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C5fe01fe2b4ab4a0be6a308d91a567fb5%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637569781081015823%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=nbTvitI61qO%2BLeD2t7XgTzvdnySYxdHuXSt36ejY7kY%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Wed May 19 07:57:19 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 19 May 2021 05:57:19 +0000 Subject: [M3devel] case vs. object method? Message-ID: m3front code like this: (*EXPORTED*) PROCEDURE IsStructured (t: T): BOOLEAN = (* Always represented as an address on the CG stack (record, array, or large set) *) (* PRE: t need not be checked. *) BEGIN IF t = NIL THEN RETURN FALSE END; CASE t.info.class OF | Class.Packed => RETURN IsStructured (Base (t)); | Class.Record, Class.Array, Class.OpenArray => RETURN TRUE; | Class.Set => RETURN (Check(t).info.size > Target.Word.size); ELSE RETURN FALSE; END; END IsStructured; Do we all think, this should really read: RETURN t # NIL AND t.isStructured() leaving the other details to subtypes of Type.T? I tend to think that. Perhaps the nil check isn't really needed, and callers can just say t.isStructured, but that opens up more subtle perhaps more controversial questions, i.e. whether or not the object-ness of T is a public part of its interface or a hidden implementation detail. My question is more about ideal code organization for maintainability, not ideal abstractions. Though both questions are valid. Is m3front this way, due to being written in Modula-2 originally? Perhaps Modula-2 having less/no support for "objects"? Or for perf? Yes, it is undeniably easier to optimize this form. But I don't think it is significant to decide based on that (modern branch predictors even handle dynamic calls, even considering the preceding dynamic path, amazing). Or because people did/do believe switches are better than, um, virtual function calls (object methods, etc.) And, if we agree, maybe we should clean it up? I realize it works fine, isn't broken. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Wed May 19 08:45:20 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 19 May 2021 06:45:20 +0000 Subject: [M3devel] declare_param typenames In-Reply-To: References: <20210504090628.FAB8D5DC@m0117565.ppops.net> , <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop>, Message-ID: > Anyway, on a more practical note.. Fyi..While that question stands, for evolution of M3CG..I have an idea, involving undoing all my recent M3CG changes. I think the existing typename feature can suffice. I still need similar m3front change, keeping original and reduced types, but then, for the typeids, I'd pass "original type". - Jay ________________________________ From: Jay K Sent: Wednesday, May 19, 2021 3:28 AM To: rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames > The language is very clear that a type is defined structurally Does that matter to backends? Not clear. Presumably the integrated backend does nothing with types. I realize m3cc uses the uids a lot, at least for m3gdb purposes. I don't remember if otherwise. The C backend presently converts them to strings, and, you know, there is a vigorous amount of casting in the IR, so types probably don't need to match. Though I would like to improve that. Perhaps generating almost readable idiomatic C, but that is unlikely or far off. Is it the right design? Not clear. It appears it was very much debated and this didn't win by much. There are advantages and disadvantages either way, esp. depending on context. C++ for its part is a mix, and does not imho provide all the desired functionality. Nor does Modula-3 provide all the desired functionality. In particular I would like: TYPE inches = INTEGER; TYPE meters = INTEGER; to optionally produce unique types. C++ has a workaround: struct inches { int value; }; struct meters { int value; }; I think Modula-3 has similar workaround but requires indirection and therefore "preferably" GC? i.e. branded pointers. Or can we have branded records w/o indirection? I was going to say C++ is name-based but that is obviously false, because people frequently say anonymous types like T* or unique_ptr which requires structural equivalence. Every struct and I believe every enum does introduce a new type. Which is certainly usually useful. typedefs, like the inches/meters example, are transparent, which is sometimes desirable, sometimes not. Also the hashes collide. So we don't quite have structural equivalence, we have structural hash equivalence. :( Right? Or maybe m3front does a more complete check, and a type-less backend like M3x86 is ok with hash collisions. At least with names, or canonical arbitrary length "string" forms of structure, you get no collisions. This is easier with C++ since you can "stop" at struct/enum. Modula-3 has to encode more information usually. I realize, like the book details, there are advantages and disadvantages either way. Most code is ok either way. Some requires one, some hypothetically requires the other. Anyway, on a more practical note.. given m3cg.foo(Typeuid) that I extend to m3cg.foo(typeuid, typename:qid) I wonder if we should combine these into one small struct. What happens if we pass even more parameters? Or heck, maybe we should really: m3cg.foo(var:fooParams). It is a tradeoff. If new params are added that most backends must not ignore, then the first form is "much" better. If new params are added just for some backends, then the second form is "much" better. I do already have records defined to store all the cg call parameters. M3C heap allocates them all and makes multiple passes. It'd be an exceedingly tedious change, to arrive back in the same place, but might be more maintainable long term (nevermind that the present form has changed very little over the very long term ? ) - Jay ________________________________ From: Rodney M. Bates Sent: Tuesday, May 18, 2021 11:41 PM To: Jay K ; rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames On 5/18/21 3:41 PM, Jay K wrote: > Fyi, This turns out to be a lot more than I realized. > But I have been able to make progress. > > The two recurring patterns are: > > - Many places a typeuid is passed to the backend, also pass a QID. > Arguably there should just be one parameter, QID or M3ID, but this is far less "explosive". > Everything keeps working easily. > I haven't done this everywhere needed yet. > > It is conceivable we should be using strings much more and hashes much less. Strings can only be used as a convenience for humans, through debuggers, etc. The language is very clear that a type is defined structurally, not by its name. There can be multiple occurrences of the same type expression, with several names and some anonymous. They are all the same type. The semantic processing has to use the hashes or equivalent to properly implement the language. The one exception is a BRANDED type with no brand string, but even here, this is explained as the language supplying an implicit brand string, not used in any other brand, so by that view, even this case is structural. > > - Many places in m3front looks like: > self.type : = something > self.type := something2(self.type) > > change to: > self.type := something > self.original_type := type > self.type := something2(self.type); > get self.original_type typename > > though possibly would work: > self.type := something > EVAL something2(self.type); > get self.type typename > > I didn't want the risk of performance or semantic change there, at least not so thoroughly. > There are places where I return original_type though, like where Formals are split and recreated (seems messy, I should look more closely..) > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Jay K > *Sent:* Tuesday, May 4, 2021 9:36 PM > *To:* rodney.m.bates at acm.org > *Cc:* m3devel at elegosoft.com > *Subject:* Re: [M3devel] declare_param typenames > I am not sure if what I want to do will help that. > > Whenever I get those errors I just rebuild from m3core and up. > I appreciate that the checking exists, at least. > It was probably the best system by far at the time it was written, > and still competitive. > > I do see similar errors from C++ LTCG/LTO about multiple sizes for same type, > and the type names are given, and C++ users and providers do want > to provide ODR violation errors, but it is a very long time coming. > I learned to not hold my breath. > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Rodney Bates > *Sent:* Tuesday, May 4, 2021 4:06 PM > *To:* Jay K ; rodney.m.bates at acm.org > *Cc:* m3devel at elegosoft.com > *Subject:* Re: [M3devel] declare_param typenames > > > -Rodney Bates > > --- jayk123 at hotmail.com wrote: > > From: Jay K > To: m3devel > Subject: [M3devel] declare_param typenames > Date: Tue, 27 Apr 2021 17:01:44 +0000 > > Fyi declare_param lacks typenames. It only has typeid. So it is a bit lossy. C backend could use better fidelity here. I intend to add a M3.QID parameter. The reader/writer can ignore it, so m3cc completely unchanged. M3x86 and M3Llvm can also ignore it, requiring a few lines. > > - Jay > > I am in favor this and similar changes. While our compilation system is quite > good at handling separate unit compilation and recompilation within a package, > the inter-package stuff emits nearly unusable error messages for inconsistencies, > missing types, etc. All they give is a UID for problem types, and not even in > the same base as elsewhere. Diagnosing such problems is a mix of sheer guesswork > and dumb luck. Mercifully, they don't happen real often, at least not to me. > > I have toyed with improving the messages in the past, but it first entails putting > type names in a number of the IR operators. Each such change has to be made in somewhere > around 10 different places, so it hasn't made it to the top of my priority list. > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C5fe01fe2b4ab4a0be6a308d91a567fb5%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637569781081015823%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=nbTvitI61qO%2BLeD2t7XgTzvdnySYxdHuXSt36ejY7kY%3D&reserved=0 > > > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C5fe01fe2b4ab4a0be6a308d91a567fb5%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637569781081015823%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=nbTvitI61qO%2BLeD2t7XgTzvdnySYxdHuXSt36ejY7kY%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Wed May 19 22:47:38 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Wed, 19 May 2021 15:47:38 -0500 Subject: [M3devel] declare_param typenames In-Reply-To: References: <20210504090628.FAB8D5DC@m0117565.ppops.net> <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop> Message-ID: On 5/18/21 10:28 PM, Jay K wrote: > ?> The language is very clear that a type is defined structurally > > Does that matter to backends? > Not clear. > > Presumably the integrated backend does nothing with types. > > I realize m3cc uses the uids a lot, at least for m3gdb purposes. > I don't remember if otherwise. > > The C backend presently converts them to strings, > and, you know, there is a vigorous amount of > casting in the IR, so types probably don't need to match. > Though I would like to improve that. > Perhaps generating almost readable idiomatic C, but that is unlikely or far off. > In addition to names, the debugger needs the type structures or hashes thereof to connect equivalent types from different compilation units and the same unit under different names. So even if a back end only uses low level types for generating code, it needs to pass the uids through for a debugger. But also, uids get stored in static data for the RTS too, so they are always needed. We have in the IR: declare_typename (t: TypeUID; n: Name); (* Associate the name 'n', in the current scope, with type 't'. There can be multiple names for a TypeUID, in multiple scopes. 'n' can contain a character-coded line number, for an anonymous type expression. *) Since there can be multiple names for the same type and some anonymous instances too, it would make sense in at least some places beyond the front end to use a uid and look up the set of type names for it. There still might be a preferred name for human consumption, but if the directly used type definition is anonymous, find a name for a different but named definition might be useful. Otherwise, a file and line number would be good. > Is it the right design? > Not clear. > It appears it was very much debated and this didn't win by much. > There are advantages and disadvantages either way, esp. > depending on context. > > C++ for its part is a mix, and does not > imho provide all the desired functionality. > Nor does Modula-3 provide all the desired functionality. > In particular I would like: > > TYPE inches = INTEGER; > TYPE meters = INTEGER; > > to optionally produce unique types. > > C++ has a workaround: > ?struct inches { int value; }; > ?struct meters { int value; }; > > I think Modula-3 has similar workaround but requires indirection > and therefore "preferably" GC? > i.e. branded pointers. > Or can we have branded records w/o indirection? > Only reference types can have brands, and for a REF RECORD, the brand is part of the structure of the REF type, not the RECORD. However, I have seen places in the RTS and/or elsewhere, that box a single scalar inside a record to create a distinct type. The field names are part of the structure of a record type, so they can be made different to distinguish. > I was going to say C++ is name-based but that is obviously > false, because people frequently say anonymous types like T* or unique_ptr > which requires structural equivalence. Someplace (more than one?) is a statement that it's name-equivalence within a compilation and structural between. This is in one way false and another, misleading. Within a compilation, the definitions that use programmer-defined type specifiers (struct, enum, etc.) have name-equivalence, and those that use declarators are structural. Between, it is indeed all structural, but here, compatibility has a different meaning than within. In every language I have ever seen besides C & C++, compatibility is a property that, if you don't have it, you will see an error message, possibly not until runtime. In this case, you won't. Rather, it's undefined, similar to a union of the types, but they are widely scattered and hard to find, unlike a union. > > Every struct and I believe every enum does introduce a new > type. Which is certainly usually useful. > typedefs, like the inches/meters example, are transparent, which > is sometimes desirable, sometimes not. BTW, "name" equivalence is a misnomer. I think every book I have ever seen says this means they have the same name. But in every specific case, TYPE T = ; TYPE U = T are "name-equivanent" while two occurrences of TYPE V = (in different scopes, of course) are not. You could eliminate the second case by defining "name" to mean fully qualified, but I've never seen anybody do that. > > Also the hashes collide. So we don't quite have structural > equivalence, we have structural hash equivalence. :( > Right? Or maybe m3front does a more complete check, and a type-less > backend like M3x86 is ok with hash collisions. Obviously, we need a better hash code. > At least with names, or canonical arbitrary length "string" forms of structure, String forms, yes. Names, no. > you get no collisions. This is easier with C++ since you can "stop" > at struct/enum. Only if a tag follows. > Modula-3 has to encode more information usually. > > I realize, like the book details, there are advantages > and disadvantages either way. Most code is ok either way. > Some requires one, some hypothetically requires the other. > > Anyway, on a more practical note.. > given m3cg.foo(Typeuid) > that I extend to m3cg.foo(typeuid, typename:qid) > > I wonder if we should combine these into one small struct. > What happens if we pass even more parameters? > Or heck, maybe we should really: > m3cg.foo(var:fooParams). > > It is a tradeoff. If new params are added that most backends must not ignore, then the first form is "much" better. > If new params are added just for some backends, then the second form is "much" better. > > I do already have records defined to store all the cg call parameters. > M3C heap allocates them all and makes multiple passes. > > It'd be an exceedingly tedious change, to arrive back in the same place, but might be more maintainable long term (nevermind that the present form has changed very little over the very long term ? ) > > ?- Jay > > -- Rodney Bates rodney.m.bates at acm.org From rodney_bates at lcwb.coop Thu May 20 00:21:17 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Wed, 19 May 2021 17:21:17 -0500 Subject: [M3devel] g++9 and g++-8 undermine debugging In-Reply-To: References: Message-ID: These cases are on two machines with different linux distributions. Compiling on the old machine, with older g++/gcc(5.4.0) and with older clang++/clang (3.8.0), all cases work. On the new machine, newer g++/gcc (9.3.0) and newer clang++/clang (10.0.0), all cases fail. In all cases, with/without the ++ makes no difference. clang or gcc makes no difference. -g/-gstabs+ makes no difference. It is the same cm3 executable on both. The .o file for the one M3 module is the same size, but has different cksum. However, objdump --full-contents on them gives identical text files. Probably a compilation time/date. The compiled _m3main.o and prog vary. All other intermediate files are identical, especially _m3main.c Those cases that fail all give the same address where the debugger was unable to insert a breakpoint at: 0x9ad. Tedious debugging of m3gdb using gdb on a succeeding case had 0xa585c for the break address. This value is called a displacement in internal variables. The age of the C/C++ compiler seems to be the only variable that makes a difference. Odd that g++ and clang++ should track each other on this. On 5/18/21 7:31 PM, Jay K wrote: > Can you elaborate on the matrix? > > gcc vs. g++ vs. clang vs. clang++ ? > ? Do any of them work? > > -g vs. -gstabs vs. other forms of -g? > ? Does anything work? > > "main in C" or not? > ? Does anything work? > I changed "it", I forgot, config or builder, so main is always in C, but I didn't remove the old code, so maybe try the other? > > Can gdb or lldb debug the C code? > ?I realize that might not matter. > > I assume g++ -g/-gstabs/etc. on non-Modula3 hello world works for you? > (well, some versions of -gstabs do have problems with C, but not what you describe.) > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* M3devel on behalf of Rodney M. Bates > *Sent:* Wednesday, May 19, 2021 12:21 AM > *To:* m3devel > *Subject:* [M3devel] g++9 and g++-8 undermine debugging > g++9, from experimental evidence, is doing something to _m3main.o > that gives m3gdb bad code addresses.? m3gdb tries to insert breakpoints > at invalid addresses and fails.? This is for breakpoints in m3-compiled > code, not in _m3main itself. > > g++-8 also fails. > > g++5 is what I have used previously, and it works. These are the only > ones I have tried and can easily try. > > -- > Rodney Bates > rodney.m.bates at acm.org > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Ce1c5fa0e6c6b42ddc1d708d91a5c16d2%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637569805084025899%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=PlZtnSlKsikiWZg9bXvH%2FLCyrGYUiZfX27GDb4oBY9k%3D&reserved=0 > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Thu May 20 01:36:47 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 19 May 2021 23:36:47 +0000 Subject: [M3devel] declare_param typenames In-Reply-To: References: <20210504090628.FAB8D5DC@m0117565.ppops.net> <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop> , Message-ID: > declare_typename Yeah, I am aware this exists, but maybe was not considering it enough. I have a new plan. I am not 100% it will work, but it might. I think I was going to require much more IR change. In my new plan, I think I can undo all of them, and put back much less, i.e. declare_procedure lacks result_typeid. i.e. We might be missing some typeids. We might even be missing some declare_typenames. But we maybe do not need to pass a typename to so many functions, except declare_typename itself. The old approach wasn't terrible though, I think. - Jay ________________________________ From: Rodney M. Bates Sent: Wednesday, May 19, 2021 8:47 PM To: Jay K ; rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames On 5/18/21 10:28 PM, Jay K wrote: > > The language is very clear that a type is defined structurally > > Does that matter to backends? > Not clear. > > Presumably the integrated backend does nothing with types. > > I realize m3cc uses the uids a lot, at least for m3gdb purposes. > I don't remember if otherwise. > > The C backend presently converts them to strings, > and, you know, there is a vigorous amount of > casting in the IR, so types probably don't need to match. > Though I would like to improve that. > Perhaps generating almost readable idiomatic C, but that is unlikely or far off. > In addition to names, the debugger needs the type structures or hashes thereof to connect equivalent types from different compilation units and the same unit under different names. So even if a back end only uses low level types for generating code, it needs to pass the uids through for a debugger. But also, uids get stored in static data for the RTS too, so they are always needed. We have in the IR: declare_typename (t: TypeUID; n: Name); (* Associate the name 'n', in the current scope, with type 't'. There can be multiple names for a TypeUID, in multiple scopes. 'n' can contain a character-coded line number, for an anonymous type expression. *) Since there can be multiple names for the same type and some anonymous instances too, it would make sense in at least some places beyond the front end to use a uid and look up the set of type names for it. There still might be a preferred name for human consumption, but if the directly used type definition is anonymous, find a name for a different but named definition might be useful. Otherwise, a file and line number would be good. > Is it the right design? > Not clear. > It appears it was very much debated and this didn't win by much. > There are advantages and disadvantages either way, esp. > depending on context. > > C++ for its part is a mix, and does not > imho provide all the desired functionality. > Nor does Modula-3 provide all the desired functionality. > In particular I would like: > > TYPE inches = INTEGER; > TYPE meters = INTEGER; > > to optionally produce unique types. > > C++ has a workaround: > struct inches { int value; }; > struct meters { int value; }; > > I think Modula-3 has similar workaround but requires indirection > and therefore "preferably" GC? > i.e. branded pointers. > Or can we have branded records w/o indirection? > Only reference types can have brands, and for a REF RECORD, the brand is part of the structure of the REF type, not the RECORD. However, I have seen places in the RTS and/or elsewhere, that box a single scalar inside a record to create a distinct type. The field names are part of the structure of a record type, so they can be made different to distinguish. > I was going to say C++ is name-based but that is obviously > false, because people frequently say anonymous types like T* or unique_ptr > which requires structural equivalence. Someplace (more than one?) is a statement that it's name-equivalence within a compilation and structural between. This is in one way false and another, misleading. Within a compilation, the definitions that use programmer-defined type specifiers (struct, enum, etc.) have name-equivalence, and those that use declarators are structural. Between, it is indeed all structural, but here, compatibility has a different meaning than within. In every language I have ever seen besides C & C++, compatibility is a property that, if you don't have it, you will see an error message, possibly not until runtime. In this case, you won't. Rather, it's undefined, similar to a union of the types, but they are widely scattered and hard to find, unlike a union. > > Every struct and I believe every enum does introduce a new > type. Which is certainly usually useful. > typedefs, like the inches/meters example, are transparent, which > is sometimes desirable, sometimes not. BTW, "name" equivalence is a misnomer. I think every book I have ever seen says this means they have the same name. But in every specific case, TYPE T = ; TYPE U = T are "name-equivanent" while two occurrences of TYPE V = (in different scopes, of course) are not. You could eliminate the second case by defining "name" to mean fully qualified, but I've never seen anybody do that. > > Also the hashes collide. So we don't quite have structural > equivalence, we have structural hash equivalence. :( > Right? Or maybe m3front does a more complete check, and a type-less > backend like M3x86 is ok with hash collisions. Obviously, we need a better hash code. > At least with names, or canonical arbitrary length "string" forms of structure, String forms, yes. Names, no. > you get no collisions. This is easier with C++ since you can "stop" > at struct/enum. Only if a tag follows. > Modula-3 has to encode more information usually. > > I realize, like the book details, there are advantages > and disadvantages either way. Most code is ok either way. > Some requires one, some hypothetically requires the other. > > Anyway, on a more practical note.. > given m3cg.foo(Typeuid) > that I extend to m3cg.foo(typeuid, typename:qid) > > I wonder if we should combine these into one small struct. > What happens if we pass even more parameters? > Or heck, maybe we should really: > m3cg.foo(var:fooParams). > > It is a tradeoff. If new params are added that most backends must not ignore, then the first form is "much" better. > If new params are added just for some backends, then the second form is "much" better. > > I do already have records defined to store all the cg call parameters. > M3C heap allocates them all and makes multiple passes. > > It'd be an exceedingly tedious change, to arrive back in the same place, but might be more maintainable long term (nevermind that the present form has changed very little over the very long term ? ) > > - Jay > > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 20 01:47:31 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 19 May 2021 23:47:31 +0000 Subject: [M3devel] g++9 and g++-8 undermine debugging In-Reply-To: References: , Message-ID: Thank you for the thoroughness. There was another factor I think you missed, which is _m3main.c can be C, or we can skip C and go right to m3cg. One of my peeves is unnecessary options and matrix expansion, and resulting lack of testing and extra work, so I years ago changed either the config or builder to always generate the C. Arguably, pedantically, this also improves C++ compatibility -- on ancient systems main needs to be in C++ to run static constructors, but nothing works that way these days, probably only cfront. The old code is definitely still there. Try it? At least for experimental data? You know, I mean, I have no idea what is the problem, and if this will fix it, but if it is does, that will be very useful as a workaround and optionally to understand the problem. Understand that the gcc developers have a passive/aggressive attitude toward stabs. I think gcc retains support, only really for AIX. But further note that gcc support for stabs..we don't really need anyway. We only need the assembler and linker to work with them. (Well, I don't...) Perhaps the assembler/linker developers have a similar attitude, and perhaps, really, the problem is in them. Can you control for gas? i.e. Can you try with old gas? Or trunk gas? Or clang's assembler? Gas is a common factor here across gcc and clang and may explain that surprise. Aside: I did find a bug in clang's assembler, that affected probably nobody but us/me, on MacOSX, when targeting older systems. That caused crashing (not debugging related). I put a workaround in our gcc fork. Specifically, it had to do with matching up imported functions and their stubs/pointers. Clang's assembler assumes they are in corresponding order, and I guess ignores their names. Gcc outputs them in hash order. So the result was the assembler producing incorrect code. I changed our gcc to sort them or something. When targeting newer MacOSX I think you don't output the stuff and the linker synthesizes them. Besides that people don't target old MacOSX, people don't often send gcc's output to clang assembler. (MacOSX only has clang asssembler, at least from Apple, ignoring Homebrew). Can you copy the .o files from the working system, then gcc/link + m3gdb on new? - Jay ________________________________ From: Rodney M. Bates Sent: Wednesday, May 19, 2021 10:21 PM To: Jay K ; m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] g++9 and g++-8 undermine debugging These cases are on two machines with different linux distributions. Compiling on the old machine, with older g++/gcc(5.4.0) and with older clang++/clang (3.8.0), all cases work. On the new machine, newer g++/gcc (9.3.0) and newer clang++/clang (10.0.0), all cases fail. In all cases, with/without the ++ makes no difference. clang or gcc makes no difference. -g/-gstabs+ makes no difference. It is the same cm3 executable on both. The .o file for the one M3 module is the same size, but has different cksum. However, objdump --full-contents on them gives identical text files. Probably a compilation time/date. The compiled _m3main.o and prog vary. All other intermediate files are identical, especially _m3main.c Those cases that fail all give the same address where the debugger was unable to insert a breakpoint at: 0x9ad. Tedious debugging of m3gdb using gdb on a succeeding case had 0xa585c for the break address. This value is called a displacement in internal variables. The age of the C/C++ compiler seems to be the only variable that makes a difference. Odd that g++ and clang++ should track each other on this. On 5/18/21 7:31 PM, Jay K wrote: > Can you elaborate on the matrix? > > gcc vs. g++ vs. clang vs. clang++ ? > Do any of them work? > > -g vs. -gstabs vs. other forms of -g? > Does anything work? > > "main in C" or not? > Does anything work? > I changed "it", I forgot, config or builder, so main is always in C, but I didn't remove the old code, so maybe try the other? > > Can gdb or lldb debug the C code? > I realize that might not matter. > > I assume g++ -g/-gstabs/etc. on non-Modula3 hello world works for you? > (well, some versions of -gstabs do have problems with C, but not what you describe.) > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* M3devel on behalf of Rodney M. Bates > *Sent:* Wednesday, May 19, 2021 12:21 AM > *To:* m3devel > *Subject:* [M3devel] g++9 and g++-8 undermine debugging > g++9, from experimental evidence, is doing something to _m3main.o > that gives m3gdb bad code addresses. m3gdb tries to insert breakpoints > at invalid addresses and fails. This is for breakpoints in m3-compiled > code, not in _m3main itself. > > g++-8 also fails. > > g++5 is what I have used previously, and it works. These are the only > ones I have tried and can easily try. > > -- > Rodney Bates > rodney.m.bates at acm.org > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cdd4c705f263343e8c9d108d91b14764a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637570596970284076%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=ZEpHl8xN1tMHBH5ulOBTfF5nDyyAKMhpMZsZh3hp4K4%3D&reserved=0 > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cdd4c705f263343e8c9d108d91b14764a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637570596970294069%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=zgac1EGofLC1fU0iXuHZhvKuXyYLm8sdNeDU%2F6j3YCA%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 20 02:33:04 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 20 May 2021 00:33:04 +0000 Subject: [M3devel] declare_param typenames In-Reply-To: References: <20210504090628.FAB8D5DC@m0117565.ppops.net> <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop> , , Message-ID: > declare_typename > I have a new plan. I am not 100% it will work, but it might. I apologize ahead of time either way..in that, mainly, I'm not sure of the new plan. I might continue down the path I was going. Might. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 19, 2021 11:36 PM To: rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames > declare_typename Yeah, I am aware this exists, but maybe was not considering it enough. I have a new plan. I am not 100% it will work, but it might. I think I was going to require much more IR change. In my new plan, I think I can undo all of them, and put back much less, i.e. declare_procedure lacks result_typeid. i.e. We might be missing some typeids. We might even be missing some declare_typenames. But we maybe do not need to pass a typename to so many functions, except declare_typename itself. The old approach wasn't terrible though, I think. - Jay ________________________________ From: Rodney M. Bates Sent: Wednesday, May 19, 2021 8:47 PM To: Jay K ; rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames On 5/18/21 10:28 PM, Jay K wrote: > > The language is very clear that a type is defined structurally > > Does that matter to backends? > Not clear. > > Presumably the integrated backend does nothing with types. > > I realize m3cc uses the uids a lot, at least for m3gdb purposes. > I don't remember if otherwise. > > The C backend presently converts them to strings, > and, you know, there is a vigorous amount of > casting in the IR, so types probably don't need to match. > Though I would like to improve that. > Perhaps generating almost readable idiomatic C, but that is unlikely or far off. > In addition to names, the debugger needs the type structures or hashes thereof to connect equivalent types from different compilation units and the same unit under different names. So even if a back end only uses low level types for generating code, it needs to pass the uids through for a debugger. But also, uids get stored in static data for the RTS too, so they are always needed. We have in the IR: declare_typename (t: TypeUID; n: Name); (* Associate the name 'n', in the current scope, with type 't'. There can be multiple names for a TypeUID, in multiple scopes. 'n' can contain a character-coded line number, for an anonymous type expression. *) Since there can be multiple names for the same type and some anonymous instances too, it would make sense in at least some places beyond the front end to use a uid and look up the set of type names for it. There still might be a preferred name for human consumption, but if the directly used type definition is anonymous, find a name for a different but named definition might be useful. Otherwise, a file and line number would be good. > Is it the right design? > Not clear. > It appears it was very much debated and this didn't win by much. > There are advantages and disadvantages either way, esp. > depending on context. > > C++ for its part is a mix, and does not > imho provide all the desired functionality. > Nor does Modula-3 provide all the desired functionality. > In particular I would like: > > TYPE inches = INTEGER; > TYPE meters = INTEGER; > > to optionally produce unique types. > > C++ has a workaround: > struct inches { int value; }; > struct meters { int value; }; > > I think Modula-3 has similar workaround but requires indirection > and therefore "preferably" GC? > i.e. branded pointers. > Or can we have branded records w/o indirection? > Only reference types can have brands, and for a REF RECORD, the brand is part of the structure of the REF type, not the RECORD. However, I have seen places in the RTS and/or elsewhere, that box a single scalar inside a record to create a distinct type. The field names are part of the structure of a record type, so they can be made different to distinguish. > I was going to say C++ is name-based but that is obviously > false, because people frequently say anonymous types like T* or unique_ptr > which requires structural equivalence. Someplace (more than one?) is a statement that it's name-equivalence within a compilation and structural between. This is in one way false and another, misleading. Within a compilation, the definitions that use programmer-defined type specifiers (struct, enum, etc.) have name-equivalence, and those that use declarators are structural. Between, it is indeed all structural, but here, compatibility has a different meaning than within. In every language I have ever seen besides C & C++, compatibility is a property that, if you don't have it, you will see an error message, possibly not until runtime. In this case, you won't. Rather, it's undefined, similar to a union of the types, but they are widely scattered and hard to find, unlike a union. > > Every struct and I believe every enum does introduce a new > type. Which is certainly usually useful. > typedefs, like the inches/meters example, are transparent, which > is sometimes desirable, sometimes not. BTW, "name" equivalence is a misnomer. I think every book I have ever seen says this means they have the same name. But in every specific case, TYPE T = ; TYPE U = T are "name-equivanent" while two occurrences of TYPE V = (in different scopes, of course) are not. You could eliminate the second case by defining "name" to mean fully qualified, but I've never seen anybody do that. > > Also the hashes collide. So we don't quite have structural > equivalence, we have structural hash equivalence. :( > Right? Or maybe m3front does a more complete check, and a type-less > backend like M3x86 is ok with hash collisions. Obviously, we need a better hash code. > At least with names, or canonical arbitrary length "string" forms of structure, String forms, yes. Names, no. > you get no collisions. This is easier with C++ since you can "stop" > at struct/enum. Only if a tag follows. > Modula-3 has to encode more information usually. > > I realize, like the book details, there are advantages > and disadvantages either way. Most code is ok either way. > Some requires one, some hypothetically requires the other. > > Anyway, on a more practical note.. > given m3cg.foo(Typeuid) > that I extend to m3cg.foo(typeuid, typename:qid) > > I wonder if we should combine these into one small struct. > What happens if we pass even more parameters? > Or heck, maybe we should really: > m3cg.foo(var:fooParams). > > It is a tradeoff. If new params are added that most backends must not ignore, then the first form is "much" better. > If new params are added just for some backends, then the second form is "much" better. > > I do already have records defined to store all the cg call parameters. > M3C heap allocates them all and makes multiple passes. > > It'd be an exceedingly tedious change, to arrive back in the same place, but might be more maintainable long term (nevermind that the present form has changed very little over the very long term ? ) > > - Jay > > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From hendrik at topoi.pooq.com Thu May 20 04:40:53 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Wed, 19 May 2021 22:40:53 -0400 Subject: [M3devel] declare_param typenames In-Reply-To: References: <20210504090628.FAB8D5DC@m0117565.ppops.net> <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop> Message-ID: <20210520024053.inl6fmnatfjyqup6@topoi.pooq.com> On Wed, May 19, 2021 at 03:47:38PM -0500, Rodney M. Bates wrote: > > In addition to names, the debugger needs the type structures or > hashes thereof to connect equivalent types from different > compilation units and the same unit under different names. Why does the debugger need to connect equivalent types from different compilation units? Can't is just use whatever name is used in the unit currently being examined? -- hendrik From rodney_bates at lcwb.coop Thu May 20 18:31:54 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Thu, 20 May 2021 11:31:54 -0500 Subject: [M3devel] declare_param typenames In-Reply-To: <20210520024053.inl6fmnatfjyqup6@topoi.pooq.com> References: <20210504090628.FAB8D5DC@m0117565.ppops.net> <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop> <20210520024053.inl6fmnatfjyqup6@topoi.pooq.com> Message-ID: <349b38ef-5528-9a09-e803-25cd9503450d@lcwb.coop> On 5/19/21 9:40 PM, Hendrik Boom wrote: > On Wed, May 19, 2021 at 03:47:38PM -0500, Rodney M. Bates wrote: >> >> In addition to names, the debugger needs the type structures or >> hashes thereof to connect equivalent types from different >> compilation units and the same unit under different names. > > Why does the debugger need to connect equivalent types from different > compilation units? Can't is just use whatever name is used in the unit > currently being examined? because it can execute assignments and calls. These should be (but aren't always) type checked. > > -- hendrik > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Fri May 21 02:45:01 2021 From: jayk123 at hotmail.com (Jay K) Date: Fri, 21 May 2021 00:45:01 +0000 Subject: [M3devel] declare_param typenames In-Reply-To: References: <20210504090628.FAB8D5DC@m0117565.ppops.net> <6171ece9-86cb-50d1-07e3-5f0632eda6d5@lcwb.coop> , , , Message-ID: Hm, well, a little bit of reworking of typenames in m3c, and then ignoring the provided QIDs did make good progress, but I think ultimately there is: Ctypes.i3 32bit TYPE int = INTEGER; TYPE long = INTEGER; foo.i3 <*external*>PROCEDURE f1():int; <*external*>PROCEDURE f2():long; which seems impossible to deal with by typenames alone. m3front must capture QIDs either at point of Modula-3 declaration and pass them along, or don't lower the types and get them right before calling m3cg. Using just typenames works well, granted, if there is a one to one mapping of hashes to names. To review, the goal is to carry typenames all the way through to C output. The above should produce: Ctypes__int foo__f1(void); Ctypes__long foo__f2(void); The ramification is a bit ugly, though, it is passing QID (or possibly string or M3ID) along with every typeid. I mean, just to restate, there can? be a many to one mapping, typename to type hash, and the names are contextualized by function signatures, variable declarations, fields...even temporaries. Function signatures is at first what I thinking should match, would suffice, but this arguably is a connected graph, assigning parameters to locals, etc. Though there are so many casts, and the intent is to have almost-identical types, maybe rest with function signatures. It is really only external function signatures at that, that I am most concerned with, so the graph is cut off, really. The full solution, all function signatures, locals, etc. makes for far more idiomatic C but maybe nobody cares. - Jay ________________________________ From: Jay K Sent: Thursday, May 20, 2021 12:33 AM To: rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames > declare_typename > I have a new plan. I am not 100% it will work, but it might. I apologize ahead of time either way..in that, mainly, I'm not sure of the new plan. I might continue down the path I was going. Might. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 19, 2021 11:36 PM To: rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames > declare_typename Yeah, I am aware this exists, but maybe was not considering it enough. I have a new plan. I am not 100% it will work, but it might. I think I was going to require much more IR change. In my new plan, I think I can undo all of them, and put back much less, i.e. declare_procedure lacks result_typeid. i.e. We might be missing some typeids. We might even be missing some declare_typenames. But we maybe do not need to pass a typename to so many functions, except declare_typename itself. The old approach wasn't terrible though, I think. - Jay ________________________________ From: Rodney M. Bates Sent: Wednesday, May 19, 2021 8:47 PM To: Jay K ; rodney.m.bates at acm.org Cc: m3devel at elegosoft.com Subject: Re: [M3devel] declare_param typenames On 5/18/21 10:28 PM, Jay K wrote: > > The language is very clear that a type is defined structurally > > Does that matter to backends? > Not clear. > > Presumably the integrated backend does nothing with types. > > I realize m3cc uses the uids a lot, at least for m3gdb purposes. > I don't remember if otherwise. > > The C backend presently converts them to strings, > and, you know, there is a vigorous amount of > casting in the IR, so types probably don't need to match. > Though I would like to improve that. > Perhaps generating almost readable idiomatic C, but that is unlikely or far off. > In addition to names, the debugger needs the type structures or hashes thereof to connect equivalent types from different compilation units and the same unit under different names. So even if a back end only uses low level types for generating code, it needs to pass the uids through for a debugger. But also, uids get stored in static data for the RTS too, so they are always needed. We have in the IR: declare_typename (t: TypeUID; n: Name); (* Associate the name 'n', in the current scope, with type 't'. There can be multiple names for a TypeUID, in multiple scopes. 'n' can contain a character-coded line number, for an anonymous type expression. *) Since there can be multiple names for the same type and some anonymous instances too, it would make sense in at least some places beyond the front end to use a uid and look up the set of type names for it. There still might be a preferred name for human consumption, but if the directly used type definition is anonymous, find a name for a different but named definition might be useful. Otherwise, a file and line number would be good. > Is it the right design? > Not clear. > It appears it was very much debated and this didn't win by much. > There are advantages and disadvantages either way, esp. > depending on context. > > C++ for its part is a mix, and does not > imho provide all the desired functionality. > Nor does Modula-3 provide all the desired functionality. > In particular I would like: > > TYPE inches = INTEGER; > TYPE meters = INTEGER; > > to optionally produce unique types. > > C++ has a workaround: > struct inches { int value; }; > struct meters { int value; }; > > I think Modula-3 has similar workaround but requires indirection > and therefore "preferably" GC? > i.e. branded pointers. > Or can we have branded records w/o indirection? > Only reference types can have brands, and for a REF RECORD, the brand is part of the structure of the REF type, not the RECORD. However, I have seen places in the RTS and/or elsewhere, that box a single scalar inside a record to create a distinct type. The field names are part of the structure of a record type, so they can be made different to distinguish. > I was going to say C++ is name-based but that is obviously > false, because people frequently say anonymous types like T* or unique_ptr > which requires structural equivalence. Someplace (more than one?) is a statement that it's name-equivalence within a compilation and structural between. This is in one way false and another, misleading. Within a compilation, the definitions that use programmer-defined type specifiers (struct, enum, etc.) have name-equivalence, and those that use declarators are structural. Between, it is indeed all structural, but here, compatibility has a different meaning than within. In every language I have ever seen besides C & C++, compatibility is a property that, if you don't have it, you will see an error message, possibly not until runtime. In this case, you won't. Rather, it's undefined, similar to a union of the types, but they are widely scattered and hard to find, unlike a union. > > Every struct and I believe every enum does introduce a new > type. Which is certainly usually useful. > typedefs, like the inches/meters example, are transparent, which > is sometimes desirable, sometimes not. BTW, "name" equivalence is a misnomer. I think every book I have ever seen says this means they have the same name. But in every specific case, TYPE T = ; TYPE U = T are "name-equivanent" while two occurrences of TYPE V = (in different scopes, of course) are not. You could eliminate the second case by defining "name" to mean fully qualified, but I've never seen anybody do that. > > Also the hashes collide. So we don't quite have structural > equivalence, we have structural hash equivalence. :( > Right? Or maybe m3front does a more complete check, and a type-less > backend like M3x86 is ok with hash collisions. Obviously, we need a better hash code. > At least with names, or canonical arbitrary length "string" forms of structure, String forms, yes. Names, no. > you get no collisions. This is easier with C++ since you can "stop" > at struct/enum. Only if a tag follows. > Modula-3 has to encode more information usually. > > I realize, like the book details, there are advantages > and disadvantages either way. Most code is ok either way. > Some requires one, some hypothetically requires the other. > > Anyway, on a more practical note.. > given m3cg.foo(Typeuid) > that I extend to m3cg.foo(typeuid, typename:qid) > > I wonder if we should combine these into one small struct. > What happens if we pass even more parameters? > Or heck, maybe we should really: > m3cg.foo(var:fooParams). > > It is a tradeoff. If new params are added that most backends must not ignore, then the first form is "much" better. > If new params are added just for some backends, then the second form is "much" better. > > I do already have records defined to store all the cg call parameters. > M3C heap allocates them all and makes multiple passes. > > It'd be an exceedingly tedious change, to arrive back in the same place, but might be more maintainable long term (nevermind that the present form has changed very little over the very long term ? ) > > - Jay > > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Fri May 21 16:54:39 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Fri, 21 May 2021 09:54:39 -0500 Subject: [M3devel] g++9 and g++-8 undermine debugging In-Reply-To: References: Message-ID: On 5/19/21 6:47 PM, Jay K wrote: > Thank you for the thoroughness. > There was another factor I think you missed, which is _m3main.c can be C, or we can skip C and go right to m3cg. > One of my peeves is unnecessary options and matrix expansion, and resulting lack of testing and extra work, so I years ago changed either the config or builder to always generate the C. Arguably, pedantically, this also improves C++ compatibility -- on ancient systems main needs to be in C++ to run static constructors, but nothing works that way these days, probably only cfront. > > The old code is definitely still there. > Try it? I found MxGen.GenerateMain,which will do it either way, but no calls on it except in Builder.GenerateCMain, which unconditionally does it in C and is called unconditionally from Builder.BuildProgram. It looks like some glue code is gone to test which way and to provide and use the needed cg_output:M3CG.T to pass to GenerateMain. There are instances of "M3_MAIN_IN_C = TRUE" in config files, but I find no uses anywhere in m3-sys. Can you point me to a commit that has what's needed? > At least for experimental data? > You know, I mean, I have no idea what is the problem, and if this will fix it, but if it is does, that will be very useful as a workaround and optionally to understand the problem. > > Understand that the gcc developers have a passive/aggressive attitude toward stabs. > I think gcc retains support, only really for AIX. > But further note that gcc support for stabs..we don't really need anyway. > We only need the assembler and linker to work with them. (Well, I don't...) > Perhaps the assembler/linker developers have a similar attitude, and perhaps, really, the problem is in them. > > Can you control for gas? > i.e. Can you try with old gas? Or trunk gas? Or clang's assembler? > Gas is a common factor here across gcc and clang and may explain that surprise. > > Aside: I did find a bug in clang's assembler, that affected probably nobody but us/me, on MacOSX, when targeting older systems. > That caused crashing (not debugging related). > I put a workaround in our gcc fork. > Specifically, it had to do with matching up imported functions and their stubs/pointers. > Clang's assembler assumes they are in corresponding order, and I guess ignores their names. > Gcc outputs them in hash order. > So the result was the assembler producing incorrect code. > I changed our gcc to sort them or something. > When targeting newer MacOSX I think you don't output the stuff and the linker synthesizes them. > Besides that people don't target old MacOSX, people don't often send gcc's output to clang assembler. (MacOSX only has clang asssembler, at least from Apple, ignoring Homebrew). > > Can you copy the .o files from the working system, then gcc/link + m3gdb on new? > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Rodney M. Bates > *Sent:* Wednesday, May 19, 2021 10:21 PM > *To:* Jay K ; m3devel ; rodney.m.bates at acm.org > *Subject:* Re: [M3devel] g++9 and g++-8 undermine debugging > These cases are on two machines with different linux distributions. > > Compiling on the old machine, with older g++/gcc(5.4.0) and with older > clang++/clang (3.8.0), all cases work. > > On the new machine, newer g++/gcc (9.3.0) and newer clang++/clang > (10.0.0), all cases fail. > > In all cases, with/without the ++ makes no difference.? clang > or gcc makes no difference.? -g/-gstabs+ makes no difference. > > It is the same cm3 executable on both.? The .o file for the one > M3 module is the same size, but has different cksum.? However, > objdump --full-contents on them gives identical text files. > Probably a compilation time/date.? The compiled _m3main.o and prog > vary.? All other intermediate files are identical, especially > _m3main.c > > Those cases that fail all give the same address where the debugger > was unable to insert a breakpoint at: 0x9ad.? Tedious debugging of > m3gdb using gdb on a succeeding case had 0xa585c for the break > address.? This value is called a displacement in internal > variables. > > The age of the C/C++ compiler seems to be the only variable that > makes a difference.? Odd that g++ and clang++ should track each > other on this. > > On 5/18/21 7:31 PM, Jay K wrote: >> Can you elaborate on the matrix? >> >> gcc vs. g++ vs. clang vs. clang++ ? >>? ? Do any of them work? >> >> -g vs. -gstabs vs. other forms of -g? >>? ? Does anything work? >> >> "main in C" or not? >>? ? Does anything work? >> I changed "it", I forgot, config or builder, so main is always in C, but I didn't remove the old code, so maybe try the other? >> >> Can gdb or lldb debug the C code? >>? ?I realize that might not matter. >> >> I assume g++ -g/-gstabs/etc. on non-Modula3 hello world works for you? >> (well, some versions of -gstabs do have problems with C, but not what you describe.) >> >>? ?- Jay >> >> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >> *From:* M3devel on behalf of Rodney M. Bates >> *Sent:* Wednesday, May 19, 2021 12:21 AM >> *To:* m3devel >> *Subject:* [M3devel] g++9 and g++-8 undermine debugging >> g++9, from experimental evidence, is doing something to _m3main.o >> that gives m3gdb bad code addresses.? m3gdb tries to insert breakpoints >> at invalid addresses and fails.? This is for breakpoints in m3-compiled >> code, not in _m3main itself. >> >> g++-8 also fails. >> >> g++5 is what I have used previously, and it works. These are the only >> ones I have tried and can easily try. >> >> -- >> Rodney Bates >> rodney.m.bates at acm.org >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cdd4c705f263343e8c9d108d91b14764a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637570596970284076%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=ZEpHl8xN1tMHBH5ulOBTfF5nDyyAKMhpMZsZh3hp4K4%3D&reserved=0 > > >> >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cdd4c705f263343e8c9d108d91b14764a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637570596970294069%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=zgac1EGofLC1fU0iXuHZhvKuXyYLm8sdNeDU%2F6j3YCA%3D&reserved=0 >> > > -- > Rodney Bates > rodney.m.bates at acm.org > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From rodney_bates at lcwb.coop Fri May 21 17:46:34 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Fri, 21 May 2021 10:46:34 -0500 Subject: [M3devel] g++9 and g++-8 undermine debugging In-Reply-To: References: Message-ID: On 5/21/21 9:54 AM, Rodney M. Bates wrote: > > > On 5/19/21 6:47 PM, Jay K wrote: >> Thank you for the thoroughness. >> There was another factor I think you missed, which is _m3main.c can be C, or we can skip C and go right to m3cg. >> One of my peeves is unnecessary options and matrix expansion, and resulting lack of testing and extra work, so I years ago changed either the config or builder to always generate the C. Arguably, pedantically, this also improves C++ compatibility -- on ancient systems main needs to be in C++ to run static constructors, but nothing works that way these days, probably only cfront. >> >> The old code is definitely still there. >> Try it? > > I found MxGen.GenerateMain,which will do it either way, but > no calls on it except in Builder.GenerateCMain, which unconditionally > does it in C and is called unconditionally from Builder.BuildProgram. > It looks like some glue code is gone to test which way and to provide > and use the needed cg_output:M3CG.T to pass to GenerateMain.? There are > instances of "M3_MAIN_IN_C = TRUE" in config files, but I find no uses > anywhere in m3-sys. > > Can you point me to a commit that has what's needed? I found it in an old tree on my local disk. Not yet sure how minimal a reinstatement I can get by with, but the needed stuff is there. > >> At least for experimental data? >> You know, I mean, I have no idea what is the problem, and if this will fix it, but if it is does, that will be very useful as a workaround and optionally to understand the problem. >> >> Understand that the gcc developers have a passive/aggressive attitude toward stabs. >> I think gcc retains support, only really for AIX. >> But further note that gcc support for stabs..we don't really need anyway. >> We only need the assembler and linker to work with them. (Well, I don't...) >> Perhaps the assembler/linker developers have a similar attitude, and perhaps, really, the problem is in them. >> >> Can you control for gas? >> i.e. Can you try with old gas? Or trunk gas? Or clang's assembler? >> Gas is a common factor here across gcc and clang and may explain that surprise. >> >> Aside: I did find a bug in clang's assembler, that affected probably nobody but us/me, on MacOSX, when targeting older systems. >> That caused crashing (not debugging related). >> I put a workaround in our gcc fork. >> Specifically, it had to do with matching up imported functions and their stubs/pointers. >> Clang's assembler assumes they are in corresponding order, and I guess ignores their names. >> Gcc outputs them in hash order. >> So the result was the assembler producing incorrect code. >> I changed our gcc to sort them or something. >> When targeting newer MacOSX I think you don't output the stuff and the linker synthesizes them. >> Besides that people don't target old MacOSX, people don't often send gcc's output to clang assembler. (MacOSX only has clang asssembler, at least from Apple, ignoring Homebrew). >> >> Can you copy the .o files from the working system, then gcc/link + m3gdb on new? >> >> ??- Jay >> >> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >> *From:* Rodney M. Bates >> *Sent:* Wednesday, May 19, 2021 10:21 PM >> *To:* Jay K ; m3devel ; rodney.m.bates at acm.org >> *Subject:* Re: [M3devel] g++9 and g++-8 undermine debugging >> These cases are on two machines with different linux distributions. >> >> Compiling on the old machine, with older g++/gcc(5.4.0) and with older >> clang++/clang (3.8.0), all cases work. >> >> On the new machine, newer g++/gcc (9.3.0) and newer clang++/clang >> (10.0.0), all cases fail. >> >> In all cases, with/without the ++ makes no difference.? clang >> or gcc makes no difference.? -g/-gstabs+ makes no difference. >> >> It is the same cm3 executable on both.? The .o file for the one >> M3 module is the same size, but has different cksum.? However, >> objdump --full-contents on them gives identical text files. >> Probably a compilation time/date.? The compiled _m3main.o and prog >> vary.? All other intermediate files are identical, especially >> _m3main.c >> >> Those cases that fail all give the same address where the debugger >> was unable to insert a breakpoint at: 0x9ad.? Tedious debugging of >> m3gdb using gdb on a succeeding case had 0xa585c for the break >> address.? This value is called a displacement in internal >> variables. >> >> The age of the C/C++ compiler seems to be the only variable that >> makes a difference.? Odd that g++ and clang++ should track each >> other on this. >> >> On 5/18/21 7:31 PM, Jay K wrote: >>> Can you elaborate on the matrix? >>> >>> gcc vs. g++ vs. clang vs. clang++ ? >>> ? ? Do any of them work? >>> >>> -g vs. -gstabs vs. other forms of -g? >>> ? ? Does anything work? >>> >>> "main in C" or not? >>> ? ? Does anything work? >>> I changed "it", I forgot, config or builder, so main is always in C, but I didn't remove the old code, so maybe try the other? >>> >>> Can gdb or lldb debug the C code? >>> ? ?I realize that might not matter. >>> >>> I assume g++ -g/-gstabs/etc. on non-Modula3 hello world works for you? >>> (well, some versions of -gstabs do have problems with C, but not what you describe.) >>> >>> ? ?- Jay >>> >>> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >>> *From:* M3devel on behalf of Rodney M. Bates >>> *Sent:* Wednesday, May 19, 2021 12:21 AM >>> *To:* m3devel >>> *Subject:* [M3devel] g++9 and g++-8 undermine debugging >>> g++9, from experimental evidence, is doing something to _m3main.o >>> that gives m3gdb bad code addresses.? m3gdb tries to insert breakpoints >>> at invalid addresses and fails.? This is for breakpoints in m3-compiled >>> code, not in _m3main itself. >>> >>> g++-8 also fails. >>> >>> g++5 is what I have used previously, and it works. These are the only >>> ones I have tried and can easily try. >>> >>> -- >>> Rodney Bates >>> rodney.m.bates at acm.org >>> _______________________________________________ >>> M3devel mailing list >>> M3devel at elegosoft.com >>> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cdd4c705f263343e8c9d108d91b14764a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637570596970284076%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=ZEpHl8xN1tMHBH5ulOBTfF5nDyyAKMhpMZsZh3hp4K4%3D&reserved=0 >> > >>> >>> _______________________________________________ >>> M3devel mailing list >>> M3devel at elegosoft.com >>> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cdd4c705f263343e8c9d108d91b14764a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637570596970294069%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=zgac1EGofLC1fU0iXuHZhvKuXyYLm8sdNeDU%2F6j3YCA%3D&reserved=0 >>> >> >> -- >> Rodney Bates >> rodney.m.bates at acm.org >> >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://m3lists.elegosoft.com/mailman/listinfo/m3devel >> > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Fri May 21 19:53:24 2021 From: jayk123 at hotmail.com (Jay K) Date: Fri, 21 May 2021 17:53:24 +0000 Subject: [M3devel] g++9 and g++-8 undermine debugging In-Reply-To: References: , Message-ID: Been over 10 years. commit d61ee80f9391bf4eaa1f02aa9c306d7d2039a7c1 Author: Jay Michael Krell Date: Tue Aug 24 05:24:24 2010 +0000 restor all the code for generating main not in C commit 1b57d6ed7649dcf6133ba65f202e2789920cba7e Author: Jay Michael Krell Date: Mon Aug 23 05:51:23 2010 +0000 Always generate main in C. My bet is more on gas, but it is worth a try. Do breakpoints in .so files work? They are not affected by this I think. - Jay ________________________________ From: Rodney M. Bates Sent: Friday, May 21, 2021 3:46 PM To: Jay K ; m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] g++9 and g++-8 undermine debugging On 5/21/21 9:54 AM, Rodney M. Bates wrote: > > > On 5/19/21 6:47 PM, Jay K wrote: >> Thank you for the thoroughness. >> There was another factor I think you missed, which is _m3main.c can be C, or we can skip C and go right to m3cg. >> One of my peeves is unnecessary options and matrix expansion, and resulting lack of testing and extra work, so I years ago changed either the config or builder to always generate the C. Arguably, pedantically, this also improves C++ compatibility -- on ancient systems main needs to be in C++ to run static constructors, but nothing works that way these days, probably only cfront. >> >> The old code is definitely still there. >> Try it? > > I found MxGen.GenerateMain,which will do it either way, but > no calls on it except in Builder.GenerateCMain, which unconditionally > does it in C and is called unconditionally from Builder.BuildProgram. > It looks like some glue code is gone to test which way and to provide > and use the needed cg_output:M3CG.T to pass to GenerateMain. There are > instances of "M3_MAIN_IN_C = TRUE" in config files, but I find no uses > anywhere in m3-sys. > > Can you point me to a commit that has what's needed? I found it in an old tree on my local disk. Not yet sure how minimal a reinstatement I can get by with, but the needed stuff is there. > >> At least for experimental data? >> You know, I mean, I have no idea what is the problem, and if this will fix it, but if it is does, that will be very useful as a workaround and optionally to understand the problem. >> >> Understand that the gcc developers have a passive/aggressive attitude toward stabs. >> I think gcc retains support, only really for AIX. >> But further note that gcc support for stabs..we don't really need anyway. >> We only need the assembler and linker to work with them. (Well, I don't...) >> Perhaps the assembler/linker developers have a similar attitude, and perhaps, really, the problem is in them. >> >> Can you control for gas? >> i.e. Can you try with old gas? Or trunk gas? Or clang's assembler? >> Gas is a common factor here across gcc and clang and may explain that surprise. >> >> Aside: I did find a bug in clang's assembler, that affected probably nobody but us/me, on MacOSX, when targeting older systems. >> That caused crashing (not debugging related). >> I put a workaround in our gcc fork. >> Specifically, it had to do with matching up imported functions and their stubs/pointers. >> Clang's assembler assumes they are in corresponding order, and I guess ignores their names. >> Gcc outputs them in hash order. >> So the result was the assembler producing incorrect code. >> I changed our gcc to sort them or something. >> When targeting newer MacOSX I think you don't output the stuff and the linker synthesizes them. >> Besides that people don't target old MacOSX, people don't often send gcc's output to clang assembler. (MacOSX only has clang asssembler, at least from Apple, ignoring Homebrew). >> >> Can you copy the .o files from the working system, then gcc/link + m3gdb on new? >> >> - Jay >> >> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >> *From:* Rodney M. Bates >> *Sent:* Wednesday, May 19, 2021 10:21 PM >> *To:* Jay K ; m3devel ; rodney.m.bates at acm.org >> *Subject:* Re: [M3devel] g++9 and g++-8 undermine debugging >> These cases are on two machines with different linux distributions. >> >> Compiling on the old machine, with older g++/gcc(5.4.0) and with older >> clang++/clang (3.8.0), all cases work. >> >> On the new machine, newer g++/gcc (9.3.0) and newer clang++/clang >> (10.0.0), all cases fail. >> >> In all cases, with/without the ++ makes no difference. clang >> or gcc makes no difference. -g/-gstabs+ makes no difference. >> >> It is the same cm3 executable on both. The .o file for the one >> M3 module is the same size, but has different cksum. However, >> objdump --full-contents on them gives identical text files. >> Probably a compilation time/date. The compiled _m3main.o and prog >> vary. All other intermediate files are identical, especially >> _m3main.c >> >> Those cases that fail all give the same address where the debugger >> was unable to insert a breakpoint at: 0x9ad. Tedious debugging of >> m3gdb using gdb on a succeeding case had 0xa585c for the break >> address. This value is called a displacement in internal >> variables. >> >> The age of the C/C++ compiler seems to be the only variable that >> makes a difference. Odd that g++ and clang++ should track each >> other on this. >> >> On 5/18/21 7:31 PM, Jay K wrote: >>> Can you elaborate on the matrix? >>> >>> gcc vs. g++ vs. clang vs. clang++ ? >>> Do any of them work? >>> >>> -g vs. -gstabs vs. other forms of -g? >>> Does anything work? >>> >>> "main in C" or not? >>> Does anything work? >>> I changed "it", I forgot, config or builder, so main is always in C, but I didn't remove the old code, so maybe try the other? >>> >>> Can gdb or lldb debug the C code? >>> I realize that might not matter. >>> >>> I assume g++ -g/-gstabs/etc. on non-Modula3 hello world works for you? >>> (well, some versions of -gstabs do have problems with C, but not what you describe.) >>> >>> - Jay >>> >>> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >>> *From:* M3devel on behalf of Rodney M. Bates >>> *Sent:* Wednesday, May 19, 2021 12:21 AM >>> *To:* m3devel >>> *Subject:* [M3devel] g++9 and g++-8 undermine debugging >>> g++9, from experimental evidence, is doing something to _m3main.o >>> that gives m3gdb bad code addresses. m3gdb tries to insert breakpoints >>> at invalid addresses and fails. This is for breakpoints in m3-compiled >>> code, not in _m3main itself. >>> >>> g++-8 also fails. >>> >>> g++5 is what I have used previously, and it works. These are the only >>> ones I have tried and can easily try. >>> >>> -- >>> Rodney Bates >>> rodney.m.bates at acm.org >>> _______________________________________________ >>> M3devel mailing list >>> M3devel at elegosoft.com >>> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C8d3aca28c5fb45bf666508d91c6faa4e%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637572088185808901%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=zPuBGdIRMAsp4wiTodVe74Uz8bQ1yWzhP6iQih9VlNE%3D&reserved=0 >> > >>> >>> _______________________________________________ >>> M3devel mailing list >>> M3devel at elegosoft.com >>> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C8d3aca28c5fb45bf666508d91c6faa4e%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637572088185818891%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=DDpsBQImEqoqtR0AM87AclkmaEKcQSij5x9pQfadYts%3D&reserved=0 >>> >> >> -- >> Rodney Bates >> rodney.m.bates at acm.org >> >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C8d3aca28c5fb45bf666508d91c6faa4e%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637572088185818891%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=DDpsBQImEqoqtR0AM87AclkmaEKcQSij5x9pQfadYts%3D&reserved=0 >> > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Fri May 21 23:38:37 2021 From: jayk123 at hotmail.com (Jay K) Date: Fri, 21 May 2021 21:38:37 +0000 Subject: [M3devel] M3cg types Message-ID: First, the C backend does work. It does not require what I am asking for. But I would like to get to a point where the C backend outputs stuff like: Mod3: Procedure f1(a:T1; var b; T2; readonly c:T3) = var d:ref t1:=nil; Begin end; C: void module__f1(module__T1 a, module__T2* b, const module__T3* c) { module__T1* d = 0; } So, passing around typename:qid does not suffice. How about something like a ?high level? type? M3CG.HiType = RECORD name:QID; indirect,readonly:BOOLEAN END; And pass these around a lot? Readonly is actually complicated in this context: Does it imply indirect? Is the pointer or pointee const? Maybe drop that for now. Point partially being, a record is easier to extend in future. The existing size, align, uid, and low level types (what to call them?) could be put in here, and existing backends would just use them. Very churny but not impossible. Thoughts? Maybe, really, it should be: M3CG.HiType = RECORD name:QID; points_to:ref HiType; readonly:BOOLEAN; END; Or, really maybe... Tony suggested m3front output LLVM IR ?directly?. Maybe we should stop being constrained by m3cg and move m3c (and m3llvm) into m3front? And therefore just use the existing structure, obviously it describes everything fairly well (except perhaps for over-eager type lowering), no need to describe stuff another way. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Fri May 21 23:46:45 2021 From: jayk123 at hotmail.com (Jay K) Date: Fri, 21 May 2021 21:46:45 +0000 Subject: [M3devel] M3cg types In-Reply-To: References: Message-ID: Ps: The way the webinfo stuff works is maybe a good model. M3front has the cg and webinfo layers that are almost but not quite like m3cg. M3C (and m3llvm) could be called from cg, and the cg, m3c (and m3llvm) interfaces could all vary a little, as needed, with no impact on m3cg and other backends. This would allow reusing the existing m3front structures more, pointers and all. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Friday, May 21, 2021 2:38:37 PM To: m3devel Subject: [M3devel] M3cg types First, the C backend does work. It does not require what I am asking for. But I would like to get to a point where the C backend outputs stuff like: Mod3: Procedure f1(a:T1; var b; T2; readonly c:T3) = var d:ref t1:=nil; Begin end; C: void module__f1(module__T1 a, module__T2* b, const module__T3* c) { module__T1* d = 0; } So, passing around typename:qid does not suffice. How about something like a ?high level? type? M3CG.HiType = RECORD name:QID; indirect,readonly:BOOLEAN END; And pass these around a lot? Readonly is actually complicated in this context: Does it imply indirect? Is the pointer or pointee const? Maybe drop that for now. Point partially being, a record is easier to extend in future. The existing size, align, uid, and low level types (what to call them?) could be put in here, and existing backends would just use them. Very churny but not impossible. Thoughts? Maybe, really, it should be: M3CG.HiType = RECORD name:QID; points_to:ref HiType; readonly:BOOLEAN; END; Or, really maybe... Tony suggested m3front output LLVM IR ?directly?. Maybe we should stop being constrained by m3cg and move m3c (and m3llvm) into m3front? And therefore just use the existing structure, obviously it describes everything fairly well (except perhaps for over-eager type lowering), no need to describe stuff another way. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sat May 22 00:06:22 2021 From: jayk123 at hotmail.com (Jay K) Date: Fri, 21 May 2021 22:06:22 +0000 Subject: [M3devel] M3cg types In-Reply-To: References: , Message-ID: Well, sorry to think so slowly, but a *possible* problem here is that m3c is kinda ?invested? in being an m3cg.t. Specifically there is m3cgMultiPass that remembers and replays the calls (kinda like M3CGWr/Rd but in memory). M3C is multiple passes, each an M3CG.T itself, sharing yet another set of data. So maybe, still, move/combine layers, so that M3front?s Type.T et. al. is usable in M3CG.T, at least without Wr/Rd removing information? One kinda hacky but cliched solution would be m3cg.hitype is just refany. Move m3c to m3front and narrow it back down to Type.T. So m3cg.t may or may not have a private interface back to m3front, and if not, e.g. m3cgMultiPass, they can still passthrough the data. Opaque + Reveal might also help. I think I will try this. - Jay ________________________________ From: Jay K Sent: Friday, May 21, 2021 2:46:45 PM To: m3devel Subject: Re: M3cg types Ps: The way the webinfo stuff works is maybe a good model. M3front has the cg and webinfo layers that are almost but not quite like m3cg. M3C (and m3llvm) could be called from cg, and the cg, m3c (and m3llvm) interfaces could all vary a little, as needed, with no impact on m3cg and other backends. This would allow reusing the existing m3front structures more, pointers and all. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Friday, May 21, 2021 2:38:37 PM To: m3devel Subject: [M3devel] M3cg types First, the C backend does work. It does not require what I am asking for. But I would like to get to a point where the C backend outputs stuff like: Mod3: Procedure f1(a:T1; var b; T2; readonly c:T3) = var d:ref t1:=nil; Begin end; C: void module__f1(module__T1 a, module__T2* b, const module__T3* c) { module__T1* d = 0; } So, passing around typename:qid does not suffice. How about something like a ?high level? type? M3CG.HiType = RECORD name:QID; indirect,readonly:BOOLEAN END; And pass these around a lot? Readonly is actually complicated in this context: Does it imply indirect? Is the pointer or pointee const? Maybe drop that for now. Point partially being, a record is easier to extend in future. The existing size, align, uid, and low level types (what to call them?) could be put in here, and existing backends would just use them. Very churny but not impossible. Thoughts? Maybe, really, it should be: M3CG.HiType = RECORD name:QID; points_to:ref HiType; readonly:BOOLEAN; END; Or, really maybe... Tony suggested m3front output LLVM IR ?directly?. Maybe we should stop being constrained by m3cg and move m3c (and m3llvm) into m3front? And therefore just use the existing structure, obviously it describes everything fairly well (except perhaps for over-eager type lowering), no need to describe stuff another way. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sat May 22 01:27:03 2021 From: jayk123 at hotmail.com (Jay K) Date: Fri, 21 May 2021 23:27:03 +0000 Subject: [M3devel] M3cg types In-Reply-To: References: , , Message-ID: Maybe the existing stuff suffices. Ignoring the const part. Though we could add declare_readonly or declare_const(uid_new, uid_target) or such. Though I still like the idea of m3cg having the full Type.T available, rather than gradually evolving M3CG.T to take more of its information. It is just that anonymous types would end up with hashes and typedefs.. Like: Mod3: Procedure f1(a:T1; var b; T2; readonly c:T3) = var d:ref t1:=nil; Begin end; C: // declare_indirect / declare_pointer to form: typedef Module__T2* T123...; typedef const Module__T3* T456...; typedef Module__T1* T789...; void module__f1(module__T1 a, T123... b, T456... c) { T789... d = 0; } But then, really, m3c could actually unwrap that, I think. When the hashes are defined as indirect, they'd get qid for the target and internally form the type text as that plus + "*", instead of just producing a typedef and using T123.. I think that is easy. Also though, it *might* be nice if we could say: TYPE varT = VAR T; PROCEDURE f(a:varT); because then I know, if I interface with C using VAR, I could use typename or QID to solve it easily. Later, - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Friday, May 21, 2021 10:06 PM To: m3devel Subject: Re: [M3devel] M3cg types Well, sorry to think so slowly, but a *possible* problem here is that m3c is kinda ?invested? in being an m3cg.t. Specifically there is m3cgMultiPass that remembers and replays the calls (kinda like M3CGWr/Rd but in memory). M3C is multiple passes, each an M3CG.T itself, sharing yet another set of data. So maybe, still, move/combine layers, so that M3front?s Type.T et. al. is usable in M3CG.T, at least without Wr/Rd removing information? One kinda hacky but cliched solution would be m3cg.hitype is just refany. Move m3c to m3front and narrow it back down to Type.T. So m3cg.t may or may not have a private interface back to m3front, and if not, e.g. m3cgMultiPass, they can still passthrough the data. Opaque + Reveal might also help. I think I will try this. - Jay ________________________________ From: Jay K Sent: Friday, May 21, 2021 2:46:45 PM To: m3devel Subject: Re: M3cg types Ps: The way the webinfo stuff works is maybe a good model. M3front has the cg and webinfo layers that are almost but not quite like m3cg. M3C (and m3llvm) could be called from cg, and the cg, m3c (and m3llvm) interfaces could all vary a little, as needed, with no impact on m3cg and other backends. This would allow reusing the existing m3front structures more, pointers and all. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Friday, May 21, 2021 2:38:37 PM To: m3devel Subject: [M3devel] M3cg types First, the C backend does work. It does not require what I am asking for. But I would like to get to a point where the C backend outputs stuff like: Mod3: Procedure f1(a:T1; var b; T2; readonly c:T3) = var d:ref t1:=nil; Begin end; C: void module__f1(module__T1 a, module__T2* b, const module__T3* c) { module__T1* d = 0; } So, passing around typename:qid does not suffice. How about something like a ?high level? type? M3CG.HiType = RECORD name:QID; indirect,readonly:BOOLEAN END; And pass these around a lot? Readonly is actually complicated in this context: Does it imply indirect? Is the pointer or pointee const? Maybe drop that for now. Point partially being, a record is easier to extend in future. The existing size, align, uid, and low level types (what to call them?) could be put in here, and existing backends would just use them. Very churny but not impossible. Thoughts? Maybe, really, it should be: M3CG.HiType = RECORD name:QID; points_to:ref HiType; readonly:BOOLEAN; END; Or, really maybe... Tony suggested m3front output LLVM IR ?directly?. Maybe we should stop being constrained by m3cg and move m3c (and m3llvm) into m3front? And therefore just use the existing structure, obviously it describes everything fairly well (except perhaps for over-eager type lowering), no need to describe stuff another way. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sat May 22 09:21:32 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 22 May 2021 07:21:32 +0000 Subject: [M3devel] segment names, require unique names? Message-ID: Backends have available something like: library (or program):cm3 (for e.g. m3-sys/cm3) source:Arg (Arg.i3 or Arg.m3) target_name:Arg.i3.c (or .s or .o, etc. the output file in current directory) Well, I added passing in those strings to M3C related to this email. and then m3cg gets: /* declare_segment name: typeid:TFFFFFFFF const:TRUE */ I would like segment names to be unique across the static link, and be valid C identifiers. Of course I can take target_name, replace dots with underscores, and prepend that to an incrementing counter. Presently I do library + source + incrementing counter. library is not needed, and this is not sufficient becauase of interface vs. module. But, ahem, is it reasonable to have m3front always name things, and meet these requirements? A less picky backend could put "static" on these things and give them simpler names, like segment and segment_const. For example, given that every module and every interface has at most one non-const segment and one const segment, m3front could say: Arg_ic (* interface, const *) Arg_i (* interface *) Arg_m (* module *) Arg_mc (* module, const *) ? I'd be fine with other little hacks, like capital I for const, capital M for const, etc. Case sensitivity is subtle but ok. Or Arg_i_data Arg_i_rdata etc. Thank you, - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sat May 22 09:27:30 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 22 May 2021 07:27:30 +0000 Subject: [M3devel] unused labels and variables in IR? Message-ID: M3c has this: (* frontend creates unreferenced labels, that gcc -Wall complains about; the point of this pass is to mark which labels are actually used, so that later set_label ignores unused labels *) and similar but for variables instead of labels. The analysis is trivial. Perhaps this really belongs in m3front? Granted, it requires another pass (M3C does multiple passes over the IR). Granted, maybe I should just pragma away these warnings, and don't care. Maybe the cost to remove them is too great in non-optimizing backends and trivially subsumed by optimizing backends? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Sun May 23 03:37:02 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Sat, 22 May 2021 20:37:02 -0500 Subject: [M3devel] case vs. object method? In-Reply-To: References: Message-ID: <05f65b12-16b4-580e-5403-5656f2c5f838@lcwb.coop> On 5/19/21 12:57 AM, Jay K wrote: > m3front code like this: > > (*EXPORTED*) > PROCEDURE IsStructured (t: T): BOOLEAN = > (* Always represented as an address on the CG stack (record, array, or large set) *) > (* PRE: t need not be checked. *) > ? BEGIN > ? ? IF t = NIL THEN RETURN FALSE END; > ? ? CASE t.info.class OF > ? ? | Class.Packed ? ?=> RETURN IsStructured (Base (t)); > ? ? | Class.Record, > ? ? ? Class.Array, > ? ? ? Class.OpenArray => RETURN TRUE; > ? ? | Class.Set ? ? ? => RETURN (Check(t).info.size > Target.Word.size); > ? ? ELSE ? ? ? ? ? ? ? ? RETURN FALSE; > ? ? END; > ? END IsStructured; > > ?Do we all think, this should really read: > > ?RETURN t # NIL AND t.isStructured() > > ?leaving the other details to subtypes of Type.T? > I tend to think that. This a judgement call, and can be complicated. I have long been an advocate for object oriented programming. I started out in the days of SIMULA 67 (anybody here old enough to remember that? Where the concepts of object orientation originated, before the term object oriented was to be later invented by others.) I was acquiring and porting SIMULA compilers and really liking it. And, leaving out quite small/simple programs, most of the things I have worked on could have used dispatching methods. That is one (not nearly all) of the reasons I was attracted to Modula-3. That said, I have watched in dismay as object orientation moved through increasing stages of buzzword-ism, fadishness, to almost cult-like proportions, culminating in the absurdity of teaching "objects first", which I liken to trying to teach babies to dance on the backs of galloping horses before they have learned to crawl. OO has advantages for some programming problems. Particularly good examples are callbacks, where the various suppliers of the callbacks need varying complements of parameters returned to them, as for example, in Thread.Fork or iterators over container data structures. Perhaps minor, but dispatching can execute almost as efficiently as a static call, probably not so much for CASE or TYPECASE. But doing maintenance on large code bodies will start to disillusion one about OO after a while. When you see a call, and if you know it is static, there is exactly one procedure it could be. In Modula-3, at least, there is usually a simple, short path to find it. The exception is when a module exports multiple interfaces. Fortunately, this doesn't happen too often. If it's a method call, there could be, and often are, many places it could go, not infrequently in the teens of them, as m3front amply shows. Moreover, not only are they widely scattered, but there is no path that leads to any of them or even discloses whether they exist. They don't even necessarily appear in interfaces, again, as m3front shows. You can only do a systematic search of everything. And despite the promise of abstraction, more often than not, a signature of a procedure or method does not have sufficient information to obviate finding the body and looking at its code. C++ makes this a lot harder because a static call can look like a dispatching one and vice versa. Also, if you need to make changes, it can be a big job. I have added a couple of new methods to one of the types in m3front a time or two, and it is frustrating to visit many places, especially when only a few need to do something different. I recall, not pleasantly, having to do this twice, when one of the overrides of a method had to dispatch to a second-level of dispatching, which, however, used a record of fields of procedure types as the actual dispatching technique. Have a look at UserProc.m3. As you point out, dispatching methods don't handle NIL the way TYPECASE does, forcing that case to be rather unorthogonally addressed before the method call. Some languages and styles avoid this by never having a NIL, instead pointing to an almost dummy-like object, but with the needed method body. Many times, I have wrestled with the decision whether to add yet another method, with umpteen overrides in umpteen locations, or code it like the example here. In this case, I would do it with the CASE, in large part because the number of case alternatives is quite a bit smaller than the number of overrides. Of course, you can provide a sort of default method body, right in the original method declaration, and only put a few overrides in places that differ. You'll still probably need to visit them all just to decide this. And this can make life even harder for the maintainer. It gets worse if there are multiple levels of subtypes, some with overrides that cover a subset of the deeper subtypes, etc. Modula-3 makes it easier to do stuff like this, because a method declaration or override points to its possibly shared body, whereas in C++, the body points to the method. Some years back, somebody said a complex, multi-level subtype hierarchy is a nightmare, despite its advocates. From my experience, I heartily agree. > > Perhaps the nil check isn't really needed, and callers can just say t.isStructured, but that opens up more subtle perhaps more controversial questions, i.e. whether or not the object-ness of T is a public part of its interface or a hidden implementation detail. > > My question is more about ideal code organization for maintainability, not ideal abstractions. > Though both questions are valid. > > Is m3front this way, due to being written in Modula-2 originally? Perhaps Modula-2 having less/no support for "objects"? > Or for perf? Yes, it is undeniably easier to optimize this form. But I don't think it is significant > ?to decide based on that (modern branch predictors even handle dynamic calls, even considering the preceding dynamic path, amazing). > > Or because people did/do believe switches are better than, um, virtual function calls (object methods, etc.) > > And, if we agree, maybe we should clean it up? I realize it works fine, isn't broken. > > ?- Jay > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From rodney_bates at lcwb.coop Sun May 23 03:37:57 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Sat, 22 May 2021 20:37:57 -0500 Subject: [M3devel] case vs. object method? In-Reply-To: References: Message-ID: <662afa27-e926-cb3e-df91-a25df1d35601@lcwb.coop> On 5/19/21 12:57 AM, Jay K wrote: > m3front code like this: > > (*EXPORTED*) > PROCEDURE IsStructured (t: T): BOOLEAN = > (* Always represented as an address on the CG stack (record, array, or large set) *) > (* PRE: t need not be checked. *) > ? BEGIN > ? ? IF t = NIL THEN RETURN FALSE END; > ? ? CASE t.info.class OF > ? ? | Class.Packed ? ?=> RETURN IsStructured (Base (t)); > ? ? | Class.Record, > ? ? ? Class.Array, > ? ? ? Class.OpenArray => RETURN TRUE; > ? ? | Class.Set ? ? ? => RETURN (Check(t).info.size > Target.Word.size); > ? ? ELSE ? ? ? ? ? ? ? ? RETURN FALSE; > ? ? END; > ? END IsStructured; > > ?Do we all think, this should really read: > > ?RETURN t # NIL AND t.isStructured() > > ?leaving the other details to subtypes of Type.T? > I tend to think that. This a judgement call, and can be complicated. I have long been an advocate for object oriented programming. I started out in the days of SIMULA 67 (anybody here old enough to remember that? Where the concepts of object orientation originated, before the term object oriented was to be later invented by others.) I was acquiring and porting SIMULA compilers and really liking it. And, leaving out quite small/simple programs, most of the things I have worked on could have used dispatching methods. That is one (not nearly all) of the reasons I was attracted to Modula-3. That said, I have watched in dismay as object orientation moved through increasing stages of buzzword-ism, fadishness, to almost cult-like proportions, culminating in the absurdity of teaching "objects first", which I liken to trying to teach babies to dance on the backs of galloping horses before they have learned to crawl. OO has advantages for some programming problems. Particularly good examples are callbacks, where the various suppliers of the callbacks need varying complements of parameters returned to them, as for example, in Thread.Fork or iterators over container data structures. Perhaps minor, but dispatching can execute almost as efficiently as a static call, probably not so much for CASE or TYPECASE. But doing maintenance on large code bodies will start to disillusion one about OO after a while. When you see a call, and if you know it is static, there is exactly one procedure it could be. In Modula-3, at least, there is usually a simple, short path to find it. The exception is when a module exports multiple interfaces. Fortunately, this doesn't happen too often. If it's a method call, there could be, and often are, many places it could go, not infrequently in the teens of them, as m3front amply shows. Moreover, not only are they widely scattered, but there is no path that leads to any of them or even discloses whether they exist. They don't even necessarily appear in interfaces, again, as m3front shows. You can only do a systematic search of everything. And despite the promise of abstraction, more often than not, a signature of a procedure or method does not have sufficient information to obviate finding the body and looking at its code. C++ makes this a lot harder because a static call can look like a dispatching one and vice versa. Also, if you need to make changes, it can be a big job. I have added a couple of new methods to one of the types in m3front a time or two, and it is frustrating to visit many places, especially when only a few need to do something different. I recall, not pleasantly, having to do this twice, when one of the overrides of a method had to dispatch to a second-level of dispatching, which, however, used a record of fields of procedure types as the actual dispatching technique. Have a look at UserProc.m3. As you point out, dispatching methods don't handle NIL the way TYPECASE does, forcing that case to be rather unorthogonally addressed before the method call. Some languages and styles avoid this by never having a NIL, instead pointing to an almost dummy-like object, but with the needed method body. Many times, I have wrestled with the decision whether to add yet another method, with umpteen overrides in umpteen locations, or code it like the example here. In this case, I would do it with the CASE, in large part because the number of case alternatives is quite a bit smaller than the number of overrides. I don't remember, I might have written that one. Of course, you can provide a sort of default method body, right in the original method declaration, and only put a few overrides in places that differ. You'll still probably need to visit them all just to decide this. And this can make life even harder for the maintainer. It gets worse if there are multiple levels of subtypes, some with overrides that cover a subset of the deeper subtypes, etc. Modula-3 makes it easier to do stuff like this, because a method declaration or override points to its possibly shared body, whereas in C++, the body points to the method. Some years back, somebody said a complex, multi-level subtype hierarchy is a nightmare, despite its advocates. From my experience, I heartily agree. > > Perhaps the nil check isn't really needed, and callers can just say t.isStructured, but that opens up more subtle perhaps more controversial questions, i.e. whether or not the object-ness of T is a public part of its interface or a hidden implementation detail. > > My question is more about ideal code organization for maintainability, not ideal abstractions. > Though both questions are valid. > > Is m3front this way, due to being written in Modula-2 originally? Perhaps Modula-2 having less/no support for "objects"? > Or for perf? Yes, it is undeniably easier to optimize this form. But I don't think it is significant > ?to decide based on that (modern branch predictors even handle dynamic calls, even considering the preceding dynamic path, amazing). > > Or because people did/do believe switches are better than, um, virtual function calls (object methods, etc.) > > And, if we agree, maybe we should clean it up? I realize it works fine, isn't broken. > > ?- Jay > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Sun May 23 06:20:07 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 23 May 2021 04:20:07 +0000 Subject: [M3devel] case vs. object method? In-Reply-To: <662afa27-e926-cb3e-df91-a25df1d35601@lcwb.coop> References: , <662afa27-e926-cb3e-df91-a25df1d35601@lcwb.coop> Message-ID: Right, kinda agreed. Function pointers defeat plain text search. Virtual functions are fairly trivially isomorphic with function pointers (except for the implicit down cast and possible adjuster thunk, both subtly valuable and underappreciated, and a reason I am not satisfied with function pointers in records.) I see there is often a 2D matrix, types x operations...or is it operations x types, is kinda the dilemna. In ?switchy? code I have found ?default? to be a big problem. You cannot easily search for an enum or such subsumed by ?default?. Agreed as well, the answer must always consider large systems. All problems are small in small systems. And the advantages of anything are often exaggerated, and large systems often ignored. Another angle would be: C has function pointers. They are used. Their use count should probably track roughly with virtual functions. But they seem to be used far less. Function pointers are harder on compilers. ?virtual? is abused by some programmers. Sprinkle it around for insurance to ease future change, it seems. Imagined cheap flexibility that is neither free nor buys much without forethought. Modula3 has a problem over C++, that the implementation of the method can be arbitrarily named. You have the extra search hop of finding the ?assignments? and then their right-sides rather than just the name. The search problem extends to record field names. Perversely K&R C fixed that. Struct fields names were in a global namespace and they all worked against all structs. Thus the ?tm_? in struct tm. It is a general problem of lexical scoping vs. global namespace. Hierarchical structure and naming, vs. plain text search. That said, I still think I favor function pointers, member functions, object methods. - Jay ________________________________ From: Rodney M. Bates Sent: Saturday, May 22, 2021 6:37:57 PM To: Jay K ; m3devel Subject: Re: [M3devel] case vs. object method? On 5/19/21 12:57 AM, Jay K wrote: > m3front code like this: > > (*EXPORTED*) > PROCEDURE IsStructured (t: T): BOOLEAN = > (* Always represented as an address on the CG stack (record, array, or large set) *) > (* PRE: t need not be checked. *) > BEGIN > IF t = NIL THEN RETURN FALSE END; > CASE t.info.class OF > | Class.Packed => RETURN IsStructured (Base (t)); > | Class.Record, > Class.Array, > Class.OpenArray => RETURN TRUE; > | Class.Set => RETURN (Check(t).info.size > Target.Word.size); > ELSE RETURN FALSE; > END; > END IsStructured; > > Do we all think, this should really read: > > RETURN t # NIL AND t.isStructured() > > leaving the other details to subtypes of Type.T? > I tend to think that. This a judgement call, and can be complicated. I have long been an advocate for object oriented programming. I started out in the days of SIMULA 67 (anybody here old enough to remember that? Where the concepts of object orientation originated, before the term object oriented was to be later invented by others.) I was acquiring and porting SIMULA compilers and really liking it. And, leaving out quite small/simple programs, most of the things I have worked on could have used dispatching methods. That is one (not nearly all) of the reasons I was attracted to Modula-3. That said, I have watched in dismay as object orientation moved through increasing stages of buzzword-ism, fadishness, to almost cult-like proportions, culminating in the absurdity of teaching "objects first", which I liken to trying to teach babies to dance on the backs of galloping horses before they have learned to crawl. OO has advantages for some programming problems. Particularly good examples are callbacks, where the various suppliers of the callbacks need varying complements of parameters returned to them, as for example, in Thread.Fork or iterators over container data structures. Perhaps minor, but dispatching can execute almost as efficiently as a static call, probably not so much for CASE or TYPECASE. But doing maintenance on large code bodies will start to disillusion one about OO after a while. When you see a call, and if you know it is static, there is exactly one procedure it could be. In Modula-3, at least, there is usually a simple, short path to find it. The exception is when a module exports multiple interfaces. Fortunately, this doesn't happen too often. If it's a method call, there could be, and often are, many places it could go, not infrequently in the teens of them, as m3front amply shows. Moreover, not only are they widely scattered, but there is no path that leads to any of them or even discloses whether they exist. They don't even necessarily appear in interfaces, again, as m3front shows. You can only do a systematic search of everything. And despite the promise of abstraction, more often than not, a signature of a procedure or method does not have sufficient information to obviate finding the body and looking at its code. C++ makes this a lot harder because a static call can look like a dispatching one and vice versa. Also, if you need to make changes, it can be a big job. I have added a couple of new methods to one of the types in m3front a time or two, and it is frustrating to visit many places, especially when only a few need to do something different. I recall, not pleasantly, having to do this twice, when one of the overrides of a method had to dispatch to a second-level of dispatching, which, however, used a record of fields of procedure types as the actual dispatching technique. Have a look at UserProc.m3. As you point out, dispatching methods don't handle NIL the way TYPECASE does, forcing that case to be rather unorthogonally addressed before the method call. Some languages and styles avoid this by never having a NIL, instead pointing to an almost dummy-like object, but with the needed method body. Many times, I have wrestled with the decision whether to add yet another method, with umpteen overrides in umpteen locations, or code it like the example here. In this case, I would do it with the CASE, in large part because the number of case alternatives is quite a bit smaller than the number of overrides. I don't remember, I might have written that one. Of course, you can provide a sort of default method body, right in the original method declaration, and only put a few overrides in places that differ. You'll still probably need to visit them all just to decide this. And this can make life even harder for the maintainer. It gets worse if there are multiple levels of subtypes, some with overrides that cover a subset of the deeper subtypes, etc. Modula-3 makes it easier to do stuff like this, because a method declaration or override points to its possibly shared body, whereas in C++, the body points to the method. Some years back, somebody said a complex, multi-level subtype hierarchy is a nightmare, despite its advocates. From my experience, I heartily agree. > > Perhaps the nil check isn't really needed, and callers can just say t.isStructured, but that opens up more subtle perhaps more controversial questions, i.e. whether or not the object-ness of T is a public part of its interface or a hidden implementation detail. > > My question is more about ideal code organization for maintainability, not ideal abstractions. > Though both questions are valid. > > Is m3front this way, due to being written in Modula-2 originally? Perhaps Modula-2 having less/no support for "objects"? > Or for perf? Yes, it is undeniably easier to optimize this form. But I don't think it is significant > to decide based on that (modern branch predictors even handle dynamic calls, even considering the preceding dynamic path, amazing). > > Or because people did/do believe switches are better than, um, virtual function calls (object methods, etc.) > > And, if we agree, maybe we should clean it up? I realize it works fine, isn't broken. > > - Jay > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C1f635a4f5b8842c2400f08d91d8b6d2a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637573306930778763%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=xpkHkltS%2B%2B7UgcG5S7wvVfavLYOMBnb7%2FT1mo2fOzNw%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sun May 23 09:15:33 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 23 May 2021 07:15:33 +0000 Subject: [M3devel] Origins In-Reply-To: References: <20210506000237.qudz47j2cgl3fola@topoi.pooq.com> <1196041620279911@mail.yandex.by> , , Message-ID: > Again, I think SRC Modula-3 2.x did output C from m3front. I saw it somewhere. https://www.linkedin.com/in/billkalsow/ "The original compiler generated C" - Jay ________________________________ From: Jay K Sent: Thursday, May 6, 2021 8:31 PM To: Mika Nystrom Cc: vvm at tut.by ; Hendrik Boom ; m3devel Subject: Re: [M3devel] Origins The question remains then, what, if any, backend preceded the gcc backend? Again, I think SRC Modula-3 2.x did output C from m3front. I saw it somewhere. Definitely quake and the "builder" were in C. The 3.6 bootstrap process had per-system assembly to build "m3front" and C for "m3build". I understand the rest, but times change. Unix and C and C++, even C++11, are ubiquitous. For bootstrap ease, C++ is a no-brainer, but the opposite, for dogfooding, using The language is an also no-brainer. Really not just C++. The optional safety might be a problem, and writing the garbage collector, but Go, Rust, Python, Java, JavaScript would all be sensible bootstrap purposes for convenience. I am not sure most of the languages provide the optional safety though. Or global variables, etc. In fact it is tempting to compile to Go and then use its garbage collector. Go is imho clearly the spiritually closest language to Modula-3 -- optionally safe, garbage collector, compiles to native. Not too many systems share those characteristics. > compile it by hand. I never quite understood this. I mean, I do technically. But 1. compile to what? One assembly? C? 2. How many times? Incrementally? In Zig I believe their plan is: A partial Zig compiler in C++. Enough to compile the Zig compiler. A full Zig compiler in Zig. Forever sticking to the subset compilable by previous. - Jay ________________________________ From: Mika Nystrom Sent: Thursday, May 6, 2021 6:46 PM To: Jay K Cc: vvm at tut.by ; Hendrik Boom ; m3devel Subject: Re: [M3devel] Origins Jay you're right that there was an early Modula-3 compiler that generated C. That compiler was not, however, what was used to build the original system and bootstrap the language. I think it was used for porting Modula-3 to non-DEC systems, well, specifically Unix systems, where C is obviously available. I am pretty sure that the systems language (ABI language or whatever you want to call it) on the WRL systems (Topaz?) that first hosted Modula-3 was Modula-2+ and not C. Not sure they used C at all on those machines. So it wouldn't have made sense to go through C... The classic approach is obviously to write the compiler in the target language and compile it by hand. But maybe Modula-3 is a bit too complicated for that! It's easy with Pascal, though---. You can use Pascal to bootstrap Modula-2, and Modula-2+ is upward compatible with Modula-2, so now you have your Modula-2+ self-hosting compiler written. From Modula-2+ to Modula-3 is only a small step. DEC had antipathies against C. If they had used a regular DEC language for the first implementation, it would have been BLISS, not C. But what would have been the point when WRL was already adept at Modula-2+? The Olivetti team that worked on m3tk I suspect were Cedar guys? From PARC? I have VMS 1.x source code somewhere, it's a thick stack of microfiche. It's all in a mixture of VAX MACRO and BLISS-32. There was a time when C wasn't everyone's systems language... Mika On Thu, May 6, 2021 at 12:57 AM Jay K > wrote: Henrik means, the implementation not the language definition. Was there a compiler in C? I suspect so. In the 3.6 release, m3build/quake were in C. In my mind, there is kinda a "sharp" tradeoff, between bootstrap (use C or C++) and "dogfooding" (use Modula-3). I realize others will strongly disagree, that it should obviously be one way, or another, or maybe yet another. I think I found a pre-3.6 release where the m3cg layer did not exist, and m3front output C a little more directly. Presumably the boot Makefiles worked at some point too? - Jay ________________________________ From: M3devel > on behalf of vvm at tut.by > Sent: Thursday, May 6, 2021 5:49 AM To: Hendrik Boom >; m3devel > Subject: Re: [M3devel] Origins Hi! I think first M3 is rebranded Modula-2+. Site Archive.org countain [Nelson91] book. But I don't sure that it describe history of Modula-3 genealogy. Best regards, Victor Miasnikov 06.05.2021, 03:03, "Hendrik Boom" >: Just curious. What was Modula 3 written in before it becae self-hosting? -- hendrik _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sun May 23 12:03:24 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 23 May 2021 10:03:24 +0000 Subject: [M3devel] full typenames vs. nested scopes? Message-ID: It turns out, in the face of nested functions, with types, we have "module" names like "2". C:\s\cm3\m3-ui\vbtkit\src\etext\MacModel.m3: /* declare_procedure name:MacModel__ArrowKey__2__extentToIRange parameter_count:3 return_type:void exported:FALSE level:1 parent:MacModel__ArrowKey return_typeid:T0 return_typename:NIL */ ... /* internal_declare_param name:end cgtype:UINT8 typeid:TA13BD21E up_level:FALSE type_text: typename:2__End */ void __cdecl MacModel__ArrowKey__2__extentToIRange( ... 2__End end_L_167, MacModel__ArrowKey_Frame_t* _static_link); This is not legal C. It isn't just that it starts with a non-character, the legality of the C, but the global uniqueness. The name is too short. So instead of qid.module := ModuleName, I should probably use GlobalName. Which returns e.g. Ctypes.int or Ctypes__int. That is, it returns the entire name, not just the module. As TEXT. Which I can M3ID.Add. Which begs the question I brought up before, about using TEXT or M3ID.T, instead of QID. Which is convenient/efficient enough for m3c, but wasteful for the rest when ignored. It could be dependent on a flag in m3middle/Target. Or, again, break through the m3cg narrow interface, and this could be Value.T, leaving M3C to calls GlobalName. Maybe passed through something like TYPE TypeInfo = RECORD ...; var typeinfo:TypeInfo? We could also pass an open array of M3ID? Thoughts? For now I can skip module names that start with integers. This does not occur within cm3 itself, where I want these "better" names. Thank you, - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From hendrik at topoi.pooq.com Sun May 23 14:07:29 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Sun, 23 May 2021 08:07:29 -0400 Subject: [M3devel] case vs. object method? In-Reply-To: <662afa27-e926-cb3e-df91-a25df1d35601@lcwb.coop> References: <662afa27-e926-cb3e-df91-a25df1d35601@lcwb.coop> Message-ID: <20210523120729.66hi4z4ukv7mfgnm@topoi.pooq.com> On Sat, May 22, 2021 at 08:37:57PM -0500, Rodney M. Bates wrote: > > I > have long been an advocate for object oriented programming. > I started out in the days of SIMULA 67 (anybody here old enough to > remember that? Where the concepts of object orientation originated, > before the term object oriented was to be later invented by others.) Yes. I remember Simula 67, though I never got a chance to use it. I got massively distracted by Algol 68 a year later. > I was acquiring and porting SIMULA compilers and really liking it. > And, leaving out quite small/simple programs, most of the things I > have worked on could have used dispatching methods. That is one > (not nearly all) of the reasons I was attracted to Modula-3. One of the things that attracted me to Modula 3 is that it seemed to provide the security of Algol 68 and was implemented on hardware I could access. But Modula 3 is far more wordy, and its words are long enough to contribute to carpal tunnel issues. OCaml is nor wordk enough, because of the prevailing style of not mentioning data types explicitly. I miss Algol 68 unions, which have been absent from just about every programming language I've had available since. Choice of type, type-checked, without extra indirection. Pascal's records and C's unions have similar storage layout, but without type-safety. > > That said, I have watched in dismay as object orientation moved > through increasing stages of buzzword-ism, fadishness, to almost > cult-like proportions, culminating in the absurdity of teaching > "objects first", which I liken to trying to teach babies to dance > on the backs of galloping horses before they have learned to crawl. Agreed. The boilerplate required by Java for the simplest Hello World code is baffles and intimidates the beginner. > > OO has advantages for some programming problems. Particularly > good examples are callbacks, where the various suppliers of the > callbacks need varying complements of parameters returned to them, as > for example, in Thread.Fork or iterators over container data structures. > Perhaps minor, but dispatching can execute almost as efficiently > as a static call, probably not so much for CASE or TYPECASE. And it's useful in simulation, where it was invented. > > But doing maintenance on large code bodies will start to disillusion > one about OO after a while. When you see a call, and if you know it > is static, there is exactly one procedure it could be. In Modula-3, > at least, there is usually a simple, short path to find it. The exception > is when a module exports multiple interfaces. Fortunately, this > doesn't happen too often. > > If it's a method call, there could be, and often are, many places it > could go, not infrequently in the teens of them, as m3front amply > shows. Moreover, not only are they widely scattered, but there is > no path that leads to any of them or even discloses whether they > exist. They don't even necessarily appear in interfaces, again, > as m3front shows. You can only do a systematic search of everything. It's necessary to have a tool that finds all possible function definitions that might be called, including the inheritance paths. Perhaps as part of a code editor? The explicit TYPECASE provides this information in the source code. > > And despite the promise of abstraction, more often than not, > a signature of a procedure or method does not have sufficient > information to obviate finding the body and looking at its code. usually you do want to know what the code does. > > C++ makes this a lot harder because a static call can look like > a dispatching one and vice versa. I never noticed that, but true. I have noticed that I very oftern explicitly write "this->" when I do a local method call. > > Also, if you need to make changes, it can be a big job. I have > added a couple of new methods to one of the types in m3front a > time or two, and it is frustrating to visit many places, especially > when only a few need to do something different. I recall, not > pleasantly, having to do this twice, when one of the overrides of > a method had to dispatch to a second-level of dispatching, which, > however, used a record of fields of procedure types as the actual > dispatching technique. Have a look at UserProc.m3. Having to declare all methods including the private ones, in a header file is also unpleasant. > > As you point out, dispatching methods don't handle NIL the way > TYPECASE does, forcing that case to be rather unorthogonally > addressed before the method call. Some languages and styles > avoid this by never having a NIL, instead pointing to an almost > dummy-like object, but with the needed method body. Nullability should be part of the type. Pointer that can be NIL should be a different, but likely related, type from pointer that cannot be NIL. > > Many times, I have wrestled with the decision whether to add > yet another method, with umpteen overrides in umpteen locations, > or code it like the example here. In this case, I would do it with > the CASE, in large part because the number of case alternatives > is quite a bit smaller than the number of overrides. I don't > remember, I might have written that one. > > Of course, you can provide a sort of default method body, right > in the original method declaration, and only put a few overrides > in places that differ. I've done that in C++. > You'll still probably need to visit them > all just to decide this. And I did have to. > And this can make life even harder for > the maintainer. It gets worse if there are multiple levels of > subtypes, some with overrides that cover a subset of the deeper > subtypes, etc. Modula-3 makes it easier to do stuff like this, > because a method declaration or override points to its possibly > shared body, whereas in C++, the body points to the method. Yes. I've always wanted to be able to code the methods right in the record (or structure) definition, and not have to point i either direction. C++'s convention was that this implies inline expansion. (Don't know if this is still relevant in practice.) > > Some years back, somebody said a complex, multi-level subtype > hierarchy is a nightmare, despite its advocates. From my > experience, I heartily agree. Which is why the printed paper books about Java had inheritance diagrams at the start of every chapter. -- hendrik From hendrik at topoi.pooq.com Sun May 23 14:17:53 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Sun, 23 May 2021 08:17:53 -0400 Subject: [M3devel] case vs. object method? In-Reply-To: References: <662afa27-e926-cb3e-df91-a25df1d35601@lcwb.coop> Message-ID: <20210523121753.j7ieriwscxjjqisp@topoi.pooq.com> On Sun, May 23, 2021 at 04:20:07AM +0000, Jay K wrote: > Right, kinda agreed. > > Function pointers defeat plain text search. > Virtual functions are fairly trivially isomorphic with function pointers (except for the implicit down cast and possible adjuster thunk, both subtly valuable and underappreciated, and a reason I am not satisfied with function pointers in records.) > > I see there is often a 2D matrix, types x operations...or is it operations x types, is kinda the dilemna. Sometimes you just want a 2D matrix, without having to choose column major or row-major styles. > Another angle would be: C has function pointers. They are used. Their > use count should probably track roughly with virtual functions. But > they seem to be used far less. And in OCaml function pointers are used far far more. The difference is that in OCaml, function pointers always carry an environment with them. In C they never do. This is kind of a halfway step to being an object method. The language Sather uses syntactic sugar to define function pointers as particular methods of particular objects. -- hendrik From jayk123 at hotmail.com Sun May 23 14:55:43 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 23 May 2021 12:55:43 +0000 Subject: [M3devel] case vs. object method? In-Reply-To: <20210523120729.66hi4z4ukv7mfgnm@topoi.pooq.com> References: <662afa27-e926-cb3e-df91-a25df1d35601@lcwb.coop>, <20210523120729.66hi4z4ukv7mfgnm@topoi.pooq.com> Message-ID: > C++'s convention was that this implies inline expansion. Optimizing compilers ignore "inline" in terms of codegen. They pretty much always have. They know better. It remains to mean "linkage". Roughly, if the compiler does not inline, and the linker sees more than one, there is no error about the duplication. Compilers have waffled as to if they actually instantiate more than one, and then chosing where, vs. instantiating multiple, giving the linker more work to do (some people worry a lot about .obj file size and linker performance). There is another term "selectany" which makes more sense, and can be used on data also. Very opinionated humans use "inline" has a hint to their fellow maintainers implying "please keep this function small". Inline in headers (not in the class definition) is also needed by older fashioned compilers without LTCG/LTO, in order to see the definition at the call site, in order to decide to inline. And to get the inline linkage. But the meaning to the compiler remains just linkage. Modern compilers (from 20 years ago) are optionally invoked by the linker so get the global visibility without moving source code around. Inline in headers is very bad for incremental non-optimized builds. Inline in headers remains important for templates for similer not-inlined reasons -- visible to instantiate. (C++ does not require the manual instantiation and naming of Modula-3. Nevermind the old cfront thing where it looked at the link errors to decide to instantiate and try again.) "Modules" promise to fix this soon, 25 years late. And then compile fast. Also decades late after having cost so much. ? - Jay ________________________________ From: M3devel on behalf of Hendrik Boom Sent: Sunday, May 23, 2021 12:07 PM To: m3devel Subject: Re: [M3devel] case vs. object method? On Sat, May 22, 2021 at 08:37:57PM -0500, Rodney M. Bates wrote: > > I > have long been an advocate for object oriented programming. > I started out in the days of SIMULA 67 (anybody here old enough to > remember that? Where the concepts of object orientation originated, > before the term object oriented was to be later invented by others.) Yes. I remember Simula 67, though I never got a chance to use it. I got massively distracted by Algol 68 a year later. > I was acquiring and porting SIMULA compilers and really liking it. > And, leaving out quite small/simple programs, most of the things I > have worked on could have used dispatching methods. That is one > (not nearly all) of the reasons I was attracted to Modula-3. One of the things that attracted me to Modula 3 is that it seemed to provide the security of Algol 68 and was implemented on hardware I could access. But Modula 3 is far more wordy, and its words are long enough to contribute to carpal tunnel issues. OCaml is nor wordk enough, because of the prevailing style of not mentioning data types explicitly. I miss Algol 68 unions, which have been absent from just about every programming language I've had available since. Choice of type, type-checked, without extra indirection. Pascal's records and C's unions have similar storage layout, but without type-safety. > > That said, I have watched in dismay as object orientation moved > through increasing stages of buzzword-ism, fadishness, to almost > cult-like proportions, culminating in the absurdity of teaching > "objects first", which I liken to trying to teach babies to dance > on the backs of galloping horses before they have learned to crawl. Agreed. The boilerplate required by Java for the simplest Hello World code is baffles and intimidates the beginner. > > OO has advantages for some programming problems. Particularly > good examples are callbacks, where the various suppliers of the > callbacks need varying complements of parameters returned to them, as > for example, in Thread.Fork or iterators over container data structures. > Perhaps minor, but dispatching can execute almost as efficiently > as a static call, probably not so much for CASE or TYPECASE. And it's useful in simulation, where it was invented. > > But doing maintenance on large code bodies will start to disillusion > one about OO after a while. When you see a call, and if you know it > is static, there is exactly one procedure it could be. In Modula-3, > at least, there is usually a simple, short path to find it. The exception > is when a module exports multiple interfaces. Fortunately, this > doesn't happen too often. > > If it's a method call, there could be, and often are, many places it > could go, not infrequently in the teens of them, as m3front amply > shows. Moreover, not only are they widely scattered, but there is > no path that leads to any of them or even discloses whether they > exist. They don't even necessarily appear in interfaces, again, > as m3front shows. You can only do a systematic search of everything. It's necessary to have a tool that finds all possible function definitions that might be called, including the inheritance paths. Perhaps as part of a code editor? The explicit TYPECASE provides this information in the source code. > > And despite the promise of abstraction, more often than not, > a signature of a procedure or method does not have sufficient > information to obviate finding the body and looking at its code. usually you do want to know what the code does. > > C++ makes this a lot harder because a static call can look like > a dispatching one and vice versa. I never noticed that, but true. I have noticed that I very oftern explicitly write "this->" when I do a local method call. > > Also, if you need to make changes, it can be a big job. I have > added a couple of new methods to one of the types in m3front a > time or two, and it is frustrating to visit many places, especially > when only a few need to do something different. I recall, not > pleasantly, having to do this twice, when one of the overrides of > a method had to dispatch to a second-level of dispatching, which, > however, used a record of fields of procedure types as the actual > dispatching technique. Have a look at UserProc.m3. Having to declare all methods including the private ones, in a header file is also unpleasant. > > As you point out, dispatching methods don't handle NIL the way > TYPECASE does, forcing that case to be rather unorthogonally > addressed before the method call. Some languages and styles > avoid this by never having a NIL, instead pointing to an almost > dummy-like object, but with the needed method body. Nullability should be part of the type. Pointer that can be NIL should be a different, but likely related, type from pointer that cannot be NIL. > > Many times, I have wrestled with the decision whether to add > yet another method, with umpteen overrides in umpteen locations, > or code it like the example here. In this case, I would do it with > the CASE, in large part because the number of case alternatives > is quite a bit smaller than the number of overrides. I don't > remember, I might have written that one. > > Of course, you can provide a sort of default method body, right > in the original method declaration, and only put a few overrides > in places that differ. I've done that in C++. > You'll still probably need to visit them > all just to decide this. And I did have to. > And this can make life even harder for > the maintainer. It gets worse if there are multiple levels of > subtypes, some with overrides that cover a subset of the deeper > subtypes, etc. Modula-3 makes it easier to do stuff like this, > because a method declaration or override points to its possibly > shared body, whereas in C++, the body points to the method. Yes. I've always wanted to be able to code the methods right in the record (or structure) definition, and not have to point i either direction. C++'s convention was that this implies inline expansion. (Don't know if this is still relevant in practice.) > > Some years back, somebody said a complex, multi-level subtype > hierarchy is a nightmare, despite its advocates. From my > experience, I heartily agree. Which is why the printed paper books about Java had inheritance diagrams at the start of every chapter. -- hendrik _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Cf61ccc45dffe43d4f74608d91de36245%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637573684710827855%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=PWDbcIzMvnhtCqjl3aWQIaI7M5x5cNBF3zieMW%2FP3mk%3D&reserved=0 -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sun May 23 14:57:40 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 23 May 2021 12:57:40 +0000 Subject: [M3devel] full typenames vs. nested scopes? In-Reply-To: References: Message-ID: Also, full typenames vs. generics? Given like: C:\s\cm3\m3-libs\libm3\src\sequence\Sequence.mg GENERIC MODULE Sequence(Elem, Seq, Rep); We end up with QIDs of Elem.T, for every instance of Sequence. i.e. not unique. I think this should include the name of the generic instantiation, e.g. CharSeq.Elem.T? Maybe GlobalName fixes this too. I will check. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Sunday, May 23, 2021 10:03 AM To: m3devel Subject: [M3devel] full typenames vs. nested scopes? It turns out, in the face of nested functions, with types, we have "module" names like "2". C:\s\cm3\m3-ui\vbtkit\src\etext\MacModel.m3: /* declare_procedure name:MacModel__ArrowKey__2__extentToIRange parameter_count:3 return_type:void exported:FALSE level:1 parent:MacModel__ArrowKey return_typeid:T0 return_typename:NIL */ ... /* internal_declare_param name:end cgtype:UINT8 typeid:TA13BD21E up_level:FALSE type_text: typename:2__End */ void __cdecl MacModel__ArrowKey__2__extentToIRange( ... 2__End end_L_167, MacModel__ArrowKey_Frame_t* _static_link); This is not legal C. It isn't just that it starts with a non-character, the legality of the C, but the global uniqueness. The name is too short. So instead of qid.module := ModuleName, I should probably use GlobalName. Which returns e.g. Ctypes.int or Ctypes__int. That is, it returns the entire name, not just the module. As TEXT. Which I can M3ID.Add. Which begs the question I brought up before, about using TEXT or M3ID.T, instead of QID. Which is convenient/efficient enough for m3c, but wasteful for the rest when ignored. It could be dependent on a flag in m3middle/Target. Or, again, break through the m3cg narrow interface, and this could be Value.T, leaving M3C to calls GlobalName. Maybe passed through something like TYPE TypeInfo = RECORD ...; var typeinfo:TypeInfo? We could also pass an open array of M3ID? Thoughts? For now I can skip module names that start with integers. This does not occur within cm3 itself, where I want these "better" names. Thank you, - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 24 05:52:22 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 03:52:22 +0000 Subject: [M3devel] modula3 coroutines Message-ID: This Modula-3 coroutine does not make sense to me. #define STACK_GAP 256 void * ContextC__PushContext(Context *c) { AUTO char a[STACK_GAP]; (void)memset(a, 0, STACK_GAP); return ContextC__PushContext1(c); } - The number 256 comes from nowhere - Any optimizing compiler will remove it. You have a better chance against the optimizer with alloca. And/or pragma to turn off optimization. But where does the number 256 come from? Since the code is only for Linux/amd64, maybe use some assembly?? Is there a more portable way? Maybe based more closely on some of the user threads code? (Which I largely wrote, but Digital is credited, 'cause I leave those in..which I didn't really write, it is based on the paper by the GNU pth authors, it says, where sigaltstack is used..er..and the makecontext form..I think makecontext is probably the way to go here. You could even make it portable with Win32 fibers presumably. The problem with these user mode threads, is not only do you have to write a scheduler, you also have to write locks. Anyway, this 256 gap thing makes no sense. I've also seen Modula3 be pretty crashy on BSD lately. I wonder if the coroutine stuff broke it???? I haven't debugged..BSD might really be dead now. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 24 05:59:48 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 03:59:48 +0000 Subject: [M3devel] converging 32bit and 64bit? Message-ID: I am wondering, why do people use 32bit systems? - The hardware they have? I haven't seen 32bit only hardware much in almost 20 years, except phone, and even that is a few years. - The OS they have installed? I haven't used 32bit OS much in 20 years, except phone, ditto. - less memory? I guess. - Interop with existing code that truncates pointers to 32 bits? Fix it already? - Interop with some assembly or a compiler or JIT? Port it already? - Interop with file formats with 32bit sizes? Port it already? - x87 floating point? An oddball, but maybe. - Nostalgia; hey it is better than 16bits? I have this idea.. like... union { void* p; int64 i; } p; do that everywhere you have a pointer. or even just i and cast every read/write to/from a pointer, which is basically what the code does anyway. I think if I tell the frontend and C backend to target 64bits, including a 64bit INTEGER, but tell the C compiler 32, it will all/mostly just work. It will waste some memory. That part is true. The difference might be some code that tries to allocate a very large fraction of address space, like a quarter or a half, and get the path "wrong". So we can distribute just 64bit C and it'll work fine on 32bit. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 24 06:38:20 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 04:38:20 +0000 Subject: [M3devel] modula3 coroutines In-Reply-To: References: Message-ID: Here is an example of the code not doing at all what the comments say: __attribute__((noinline)) // I added this. static void * stack_here(void) { AUTO char *top=(char *)⊤ return top; } __attribute__((noinline)) // I added this. static void * ContextC__PushContext1(Context *c) { AUTO ucontext_t uc=c->uc; /* write it on the stack */ void *top; top = stack_here(); return top; } compiles to: stack_here: xorl %eax, %eax ret ContextC__PushContext1.isra.0: jmp stack_here - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Monday, May 24, 2021 3:52 AM To: m3devel Subject: [M3devel] modula3 coroutines This Modula-3 coroutine does not make sense to me. #define STACK_GAP 256 void * ContextC__PushContext(Context *c) { AUTO char a[STACK_GAP]; (void)memset(a, 0, STACK_GAP); return ContextC__PushContext1(c); } - The number 256 comes from nowhere - Any optimizing compiler will remove it. You have a better chance against the optimizer with alloca. And/or pragma to turn off optimization. But where does the number 256 come from? Since the code is only for Linux/amd64, maybe use some assembly?? Is there a more portable way? Maybe based more closely on some of the user threads code? (Which I largely wrote, but Digital is credited, 'cause I leave those in..which I didn't really write, it is based on the paper by the GNU pth authors, it says, where sigaltstack is used..er..and the makecontext form..I think makecontext is probably the way to go here. You could even make it portable with Win32 fibers presumably. The problem with these user mode threads, is not only do you have to write a scheduler, you also have to write locks. Anyway, this 256 gap thing makes no sense. I've also seen Modula3 be pretty crashy on BSD lately. I wonder if the coroutine stuff broke it???? I haven't debugged..BSD might really be dead now. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 24 06:52:14 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 04:52:14 +0000 Subject: [M3devel] modula3 coroutines In-Reply-To: References: , Message-ID: Ok, there are comments elsewhere. The intention is enough stack for SetCoStack... let me see about that.. - Jay ________________________________ From: Jay K Sent: Monday, May 24, 2021 4:38 AM To: m3devel ; Mika Nystrom Subject: Re: modula3 coroutines Here is an example of the code not doing at all what the comments say: __attribute__((noinline)) // I added this. static void * stack_here(void) { AUTO char *top=(char *)⊤ return top; } __attribute__((noinline)) // I added this. static void * ContextC__PushContext1(Context *c) { AUTO ucontext_t uc=c->uc; /* write it on the stack */ void *top; top = stack_here(); return top; } compiles to: stack_here: xorl %eax, %eax ret ContextC__PushContext1.isra.0: jmp stack_here - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Monday, May 24, 2021 3:52 AM To: m3devel Subject: [M3devel] modula3 coroutines This Modula-3 coroutine does not make sense to me. #define STACK_GAP 256 void * ContextC__PushContext(Context *c) { AUTO char a[STACK_GAP]; (void)memset(a, 0, STACK_GAP); return ContextC__PushContext1(c); } - The number 256 comes from nowhere - Any optimizing compiler will remove it. You have a better chance against the optimizer with alloca. And/or pragma to turn off optimization. But where does the number 256 come from? Since the code is only for Linux/amd64, maybe use some assembly?? Is there a more portable way? Maybe based more closely on some of the user threads code? (Which I largely wrote, but Digital is credited, 'cause I leave those in..which I didn't really write, it is based on the paper by the GNU pth authors, it says, where sigaltstack is used..er..and the makecontext form..I think makecontext is probably the way to go here. You could even make it portable with Win32 fibers presumably. The problem with these user mode threads, is not only do you have to write a scheduler, you also have to write locks. Anyway, this 256 gap thing makes no sense. I've also seen Modula3 be pretty crashy on BSD lately. I wonder if the coroutine stuff broke it???? I haven't debugged..BSD might really be dead now. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 24 07:05:11 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 05:05:11 +0000 Subject: [M3devel] modula3 coroutines In-Reply-To: References: , , Message-ID: ThreadPThread.IncInCritical(); WITH top = ContextC.PushContext(me.context) DO ThreadPThread.SetCoStack(to.gcstack, top) END; Once you are inCritical, is the order of the rest critical? That is, could SetCoStack return an address, that this caller ("Call") can write to? Thereby removing the need for the stack gap? Furthermore, does PushContext need to return? Or it could it continue by calling another function, "the rest of 'Call'"? Are there good tests for this? I don't really want to change unless I can test it well.. - Jay ________________________________ From: Jay K Sent: Monday, May 24, 2021 4:52 AM To: m3devel ; Mika Nystrom Subject: Re: modula3 coroutines Ok, there are comments elsewhere. The intention is enough stack for SetCoStack... let me see about that.. - Jay ________________________________ From: Jay K Sent: Monday, May 24, 2021 4:38 AM To: m3devel ; Mika Nystrom Subject: Re: modula3 coroutines Here is an example of the code not doing at all what the comments say: __attribute__((noinline)) // I added this. static void * stack_here(void) { AUTO char *top=(char *)⊤ return top; } __attribute__((noinline)) // I added this. static void * ContextC__PushContext1(Context *c) { AUTO ucontext_t uc=c->uc; /* write it on the stack */ void *top; top = stack_here(); return top; } compiles to: stack_here: xorl %eax, %eax ret ContextC__PushContext1.isra.0: jmp stack_here - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Monday, May 24, 2021 3:52 AM To: m3devel Subject: [M3devel] modula3 coroutines This Modula-3 coroutine does not make sense to me. #define STACK_GAP 256 void * ContextC__PushContext(Context *c) { AUTO char a[STACK_GAP]; (void)memset(a, 0, STACK_GAP); return ContextC__PushContext1(c); } - The number 256 comes from nowhere - Any optimizing compiler will remove it. You have a better chance against the optimizer with alloca. And/or pragma to turn off optimization. But where does the number 256 come from? Since the code is only for Linux/amd64, maybe use some assembly?? Is there a more portable way? Maybe based more closely on some of the user threads code? (Which I largely wrote, but Digital is credited, 'cause I leave those in..which I didn't really write, it is based on the paper by the GNU pth authors, it says, where sigaltstack is used..er..and the makecontext form..I think makecontext is probably the way to go here. You could even make it portable with Win32 fibers presumably. The problem with these user mode threads, is not only do you have to write a scheduler, you also have to write locks. Anyway, this 256 gap thing makes no sense. I've also seen Modula3 be pretty crashy on BSD lately. I wonder if the coroutine stuff broke it???? I haven't debugged..BSD might really be dead now. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 24 08:26:57 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 06:26:57 +0000 Subject: [M3devel] modula3 coroutines In-Reply-To: References: , , , Message-ID: Something like this maybe? m3core: Coroutines: remove the need for one function to try to allocate stack space for another function. by jaykrell ? Pull Request #524 ? modula3/cm3 (github.com) It builds at least. I didn't see test cases. - Jay ________________________________ From: Jay K Sent: Monday, May 24, 2021 5:05 AM To: m3devel ; Mika Nystrom Subject: Re: modula3 coroutines ThreadPThread.IncInCritical(); WITH top = ContextC.PushContext(me.context) DO ThreadPThread.SetCoStack(to.gcstack, top) END; Once you are inCritical, is the order of the rest critical? That is, could SetCoStack return an address, that this caller ("Call") can write to? Thereby removing the need for the stack gap? Furthermore, does PushContext need to return? Or it could it continue by calling another function, "the rest of 'Call'"? Are there good tests for this? I don't really want to change unless I can test it well.. - Jay ________________________________ From: Jay K Sent: Monday, May 24, 2021 4:52 AM To: m3devel ; Mika Nystrom Subject: Re: modula3 coroutines Ok, there are comments elsewhere. The intention is enough stack for SetCoStack... let me see about that.. - Jay ________________________________ From: Jay K Sent: Monday, May 24, 2021 4:38 AM To: m3devel ; Mika Nystrom Subject: Re: modula3 coroutines Here is an example of the code not doing at all what the comments say: __attribute__((noinline)) // I added this. static void * stack_here(void) { AUTO char *top=(char *)⊤ return top; } __attribute__((noinline)) // I added this. static void * ContextC__PushContext1(Context *c) { AUTO ucontext_t uc=c->uc; /* write it on the stack */ void *top; top = stack_here(); return top; } compiles to: stack_here: xorl %eax, %eax ret ContextC__PushContext1.isra.0: jmp stack_here - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Monday, May 24, 2021 3:52 AM To: m3devel Subject: [M3devel] modula3 coroutines This Modula-3 coroutine does not make sense to me. #define STACK_GAP 256 void * ContextC__PushContext(Context *c) { AUTO char a[STACK_GAP]; (void)memset(a, 0, STACK_GAP); return ContextC__PushContext1(c); } - The number 256 comes from nowhere - Any optimizing compiler will remove it. You have a better chance against the optimizer with alloca. And/or pragma to turn off optimization. But where does the number 256 come from? Since the code is only for Linux/amd64, maybe use some assembly?? Is there a more portable way? Maybe based more closely on some of the user threads code? (Which I largely wrote, but Digital is credited, 'cause I leave those in..which I didn't really write, it is based on the paper by the GNU pth authors, it says, where sigaltstack is used..er..and the makecontext form..I think makecontext is probably the way to go here. You could even make it portable with Win32 fibers presumably. The problem with these user mode threads, is not only do you have to write a scheduler, you also have to write locks. Anyway, this 256 gap thing makes no sense. I've also seen Modula3 be pretty crashy on BSD lately. I wonder if the coroutine stuff broke it???? I haven't debugged..BSD might really be dead now. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From lemming at henning-thielemann.de Mon May 24 11:00:50 2021 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Mon, 24 May 2021 11:00:50 +0200 (CEST) Subject: [M3devel] converging 32bit and 64bit? In-Reply-To: References: Message-ID: <4ccfce10-9c68-dc1e-d6b9-bcee836b1fb7@henning-thielemann.de> On Mon, 24 May 2021, Jay K wrote: > I am wondering, why do people use 32bit systems? > ?- The hardware they have? > ? I haven't seen 32bit only hardware much in almost 20 years, except phone, and even that is a few years. eeepc, raspberry pi, embedded systems From hendrik at topoi.pooq.com Mon May 24 14:19:00 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Mon, 24 May 2021 08:19:00 -0400 Subject: [M3devel] modula3 coroutines In-Reply-To: References: Message-ID: <20210524121900.x4de7ofuhl3jh6ht@topoi.pooq.com> On Mon, May 24, 2021 at 04:38:20AM +0000, Jay K wrote: > Here is an example of the code not doing at all what the comments say: > > __attribute__((noinline)) // I added this. > static void * > stack_here(void) > { > AUTO char *top=(char *)⊤ > return top; > } > > __attribute__((noinline)) // I added this. > static void * > ContextC__PushContext1(Context *c) > { > AUTO ucontext_t uc=c->uc; /* write it on the stack */ > void *top; > > top = stack_here(); > return top; > } > > compiles to: > > stack_here: > xorl %eax, %eax > ret > > ContextC__PushContext1.isra.0: > jmp stack_here > > - Jay I'm guessing that this is C code, and that AUTO turns into something that indicates stack allocatin of the variable being declared, and that %eax is the register used for functions to return void* values. How is this a egitimate thing for a compiler to do? -- returning NULL when the source code clearly does not do that. I'd say report a bug against the C compiler. Would it help to declare top volatile? -- hendrik From hendrik at topoi.pooq.com Mon May 24 14:19:57 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Mon, 24 May 2021 08:19:57 -0400 Subject: [M3devel] modula3 coroutines In-Reply-To: References: Message-ID: <20210524121957.spr5rt76mvdqnolj@topoi.pooq.com> On Mon, May 24, 2021 at 03:52:22AM +0000, Jay K wrote: > This Modula-3 coroutine does not make sense to me. > #define STACK_GAP 256 > > void * > ContextC__PushContext(Context *c) > { > AUTO char a[STACK_GAP]; > > (void)memset(a, 0, STACK_GAP); > > return ContextC__PushContext1(c); > } > > - The number 256 comes from nowhere > - Any optimizing compiler will remove it. Would it help to declare a volatile? -- hendrik From hendrik at topoi.pooq.com Mon May 24 14:26:27 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Mon, 24 May 2021 08:26:27 -0400 Subject: [M3devel] converging 32bit and 64bit? In-Reply-To: <4ccfce10-9c68-dc1e-d6b9-bcee836b1fb7@henning-thielemann.de> References: <4ccfce10-9c68-dc1e-d6b9-bcee836b1fb7@henning-thielemann.de> Message-ID: <20210524122627.m2gqr32wzqgtdabo@topoi.pooq.com> On Mon, May 24, 2021 at 11:00:50AM +0200, Henning Thielemann wrote: > > On Mon, 24 May 2021, Jay K wrote: > > > I am wondering, why do people use 32bit systems? > > ?- The hardware they have? > > ? I haven't seen 32bit only hardware much in almost 20 years, except phone, and even that is a few years. > > eeepc, raspberry pi, embedded systems The 32-bit machine I mentioned in another branch of this thread is indeed an eeepc. It is still a wonderfully durable and reliable machine. -- hendrik From jayk123 at hotmail.com Mon May 24 17:17:25 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 15:17:25 +0000 Subject: [M3devel] converging 32bit and 64bit? In-Reply-To: <4ccfce10-9c68-dc1e-d6b9-bcee836b1fb7@henning-thielemann.de> References: , <4ccfce10-9c68-dc1e-d6b9-bcee836b1fb7@henning-thielemann.de> Message-ID: Do you run Modula3 on those? Well, nobody so far said directly to use less memory, so we can try that, halving the build matrix. - Jay ________________________________ From: Henning Thielemann Sent: Monday, May 24, 2021 2:00:50 AM To: Jay K Cc: m3devel Subject: Re: [M3devel] converging 32bit and 64bit? On Mon, 24 May 2021, Jay K wrote: > I am wondering, why do people use 32bit systems? > - The hardware they have? > I haven't seen 32bit only hardware much in almost 20 years, except phone, and even that is a few years. eeepc, raspberry pi, embedded systems -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 24 22:56:22 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 20:56:22 +0000 Subject: [M3devel] Word.T rules vs. enforcement? Message-ID: This Word.T business seems bad to me. In that, it has plenty of rules, and no enforcement. I get that assignment is pass through, works. And compare for equality/inequality are ok. But you are never supposed to use e.g. "+" on Word.T. But yet, "+" does work on Word.T. It compiles. It is usually correct. But it could go wrong for overflow. And then "<" compiles, and works sometimes, and is wrong. Is there a better way? I'd suggest REFANY or traced pointer to branded record, but these have GC implications. And untraced pointer is unsafe. So stuck? How about a record containing an integer, passed around by value? That is still abusable but with more effort. It could even be an array of char to dissuade manual use, that the implementation could cast away. Of course, hoping/assuming the ABI deals efficiently with small records. And then I have to decide what type C should use for Word.T. I think it should actually be unsigned. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 24 23:31:35 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 21:31:35 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: Message-ID: Oops, actually + is correct, assuming integer overflow is well defined..which I believe we are unfortunately ambivalent about. :( - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Monday, May 24, 2021 1:56:22 PM To: m3devel Subject: [M3devel] Word.T rules vs. enforcement? This Word.T business seems bad to me. In that, it has plenty of rules, and no enforcement. I get that assignment is pass through, works. And compare for equality/inequality are ok. But you are never supposed to use e.g. "+" on Word.T. But yet, "+" does work on Word.T. It compiles. It is usually correct. But it could go wrong for overflow. And then "<" compiles, and works sometimes, and is wrong. Is there a better way? I'd suggest REFANY or traced pointer to branded record, but these have GC implications. And untraced pointer is unsafe. So stuck? How about a record containing an integer, passed around by value? That is still abusable but with more effort. It could even be an array of char to dissuade manual use, that the implementation could cast away. Of course, hoping/assuming the ABI deals efficiently with small records. And then I have to decide what type C should use for Word.T. I think it should actually be unsigned. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Tue May 25 00:27:17 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 24 May 2021 22:27:17 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: , Message-ID: I am not criticizing the functions and their meaning in the Word interface per se. My problem is that Word.T, the type, is not actually limited by the language/interface, to be used only with the Word interface functions. Its INTEGER-ness is transparent. That seems bad. + is as you say, "in between". If the runtime traps signed integer overflow, then +Word.T is wrong. If the runtime allows silent signed integer overflow, then +Word.T is ok. But for example < is wrong, sometimes. I think a record containing an INTEGER, or array of bytes, would be better. Even better if the language prevented anything from reading/writing the fields, except for the implementation of the interface -- basically like C++ private:, or private + friend (if the functions are not member functions). Modula-3 seems to do plenty ok with the features of heap allocated garbage collected types, but much less good with value types and stack allocated and globals. I think that is a good approximate summary -- the features are all there, just not allowed on all types, esp. not on the "efficient" types. (I also think importing FooRep is too easy, but I guess that can be limited to the library and not importers, because of lowercase interface and capital Interface in quake?) > not a machine-oriented definition. Everything needs to be somewhat machine-oriented, in order to be machine-implemented. - Jay ________________________________ From: Mika Nystrom Sent: Monday, May 24, 2021 10:02 PM To: Jay K Cc: m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? I'm not sure what the problem is here, but hmm, no + is generally not correct on Word.T. Integer overflow on INTEGERs in Modula-3 is defined as being an error, which may or may not be caught by the implementation. Usually when you use Word.T, you don't want integer overflow to be an error but to wrap. Word.T is an interface that interprets bits of type Word.T (usually = INTEGER) in a specific way. Nothing else. INTEGER is really meant to look like Peano's numbers. It's a mathematics-oriented definition, not a machine-oriented definition. That's the Algol Way. If you want the machine-oriented view (where Word.T is an integer MOD 2^WordSize and all the operations are defined on such objects), use the facilities defined in the Word interface to manipulate those (same) bits. Mika P.S. I saw your emails about semaphores. I will get to it I promise---I need to try to remember what I did there before responding. On Mon, May 24, 2021 at 2:31 PM Jay K > wrote: Oops, actually + is correct, assuming integer overflow is well defined..which I believe we are unfortunately ambivalent about. :( - Jay ________________________________ From: M3devel > on behalf of Jay K > Sent: Monday, May 24, 2021 1:56:22 PM To: m3devel > Subject: [M3devel] Word.T rules vs. enforcement? This Word.T business seems bad to me. In that, it has plenty of rules, and no enforcement. I get that assignment is pass through, works. And compare for equality/inequality are ok. But you are never supposed to use e.g. "+" on Word.T. But yet, "+" does work on Word.T. It compiles. It is usually correct. But it could go wrong for overflow. And then "<" compiles, and works sometimes, and is wrong. Is there a better way? I'd suggest REFANY or traced pointer to branded record, but these have GC implications. And untraced pointer is unsafe. So stuck? How about a record containing an integer, passed around by value? That is still abusable but with more effort. It could even be an array of char to dissuade manual use, that the implementation could cast away. Of course, hoping/assuming the ABI deals efficiently with small records. And then I have to decide what type C should use for Word.T. I think it should actually be unsigned. - Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From hendrik at topoi.pooq.com Tue May 25 05:03:37 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Mon, 24 May 2021 23:03:37 -0400 Subject: [M3devel] converging 32bit and 64bit? In-Reply-To: References: <4ccfce10-9c68-dc1e-d6b9-bcee836b1fb7@henning-thielemann.de> Message-ID: <20210525030337.gofkzg4um4sdnwlu@topoi.pooq.com> On Mon, May 24, 2021 at 03:17:25PM +0000, Jay K wrote: > Do you run Modula3 on those? I don't know if Henning Thielemann does, but I run it on an eeepc. -- hendrik > Well, nobody so far said directly to use less memory, so we can try that, halving the build matrix. > > - Jay > ________________________________ > From: Henning Thielemann > Sent: Monday, May 24, 2021 2:00:50 AM > To: Jay K > Cc: m3devel > Subject: Re: [M3devel] converging 32bit and 64bit? > > > On Mon, 24 May 2021, Jay K wrote: > > > I am wondering, why do people use 32bit systems? > > - The hardware they have? > > I haven't seen 32bit only hardware much in almost 20 years, except phone, and even that is a few years. > > eeepc, raspberry pi, embedded systems > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel From jayk123 at hotmail.com Tue May 25 07:19:26 2021 From: jayk123 at hotmail.com (Jay K) Date: Tue, 25 May 2021 05:19:26 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: , Message-ID: Aside: Here is an example of the inconsistency btw: WebInfo.m3: import_procedure name:Word__Divide return_typename:INTEGER Word.i3: import_procedure name:Word__Divide return_typename:Word__T I freely admit, return_typename is new, I added it. So no preexisting code cares. In both cases it resolves to signed INT64. So even new code is pretty ok. But I kinda do need to fix it and it is kinda wrong/bad. The reason is: - I want to concatenate all the m3c output. - Including with hand written C. Maybe this is misguided. But it'll enable redistributing a minimal cm3 system as one large file. It makes some sense on a per-library basis as well, for "old fashioned LTCG". But incremental build suffers, and it stresses the C compiler. So every m3c file should be consistent with other m3c files. i.e. if they declare the same function, it should have the same signature. Ok, so typedef something INTEGER; typedef INTEGER Word__T; But also chosen interpretation of Word.T/Word__T should make sense for hand written C. C functions should be able to take Word.T..well..maybe. Maybe no need. It isn't a big huge deal but it is something. I had actually introduced WORD_T (all caps, single underscore) for functions taking Word.T, years ago. And for internal use, as being approximately size_t or uintptr_t, which it matches on most systems (except VMS..) Perhaps having one type for Modula3 and another for C is an ok idea. Signed for Modula3, unsigned for C. But it was done by accident, and the names reflect that. I'm still not sure what to do, but there isn't an immediate problem. I am still working through more m3c/C divergences. > (BTW, other parts of the language spec ensure that the behavior > resulting from not checking the range is not an "unchecked runtime error".) Please avoid double/triple negatives. I think you are saying, that even in the face of silent integer overflow, we will not suffer any thing else, like array overflow or double free. > Yes < is not really meaningful on integers MOD 2^N. Well, what I meant was, Word.T < does a signed comparison. It is the intent that Word32.T{0x80000000} > Word32.T{0x7FFFFFFF}, but it is not. It only works if you call Word.LT. I understand your point, that in the face of silent rollover you also get the wrong result. This is the contract of Word.T and the ambivalent contract of INTEGER. Relevant aside: I think only Rust has really tried to solve this, with the obvious semi-complicated outcome: You need a bunch of types and let the user decide on a case by case basis. But hopefully good defaults. > They are inconvenient to implement on most hardware... Not at all, "just" inefficient if portable, or a few machine specific nonportable options. Too much to write here and now. > Yes, I think that's a good summary. Can/should we fix/change it? Like, should I be able to make an opaque type, that has a known constant size and is "blittable" (memcpy-able, maybe memcmp-able)? Are small records enough? - Jay ________________________________ From: Mika Nystrom Sent: Monday, May 24, 2021 11:18 PM To: Jay K Cc: m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? On Mon, May 24, 2021 at 3:27 PM Jay K > wrote: I am not criticizing the functions and their meaning in the Word interface per se. My problem is that Word.T, the type, is not actually limited by the language/interface, to be used only with the Word interface functions. Its INTEGER-ness is transparent. That seems bad. + is as you say, "in between". If the runtime traps signed integer overflow, then +Word.T is wrong. If the runtime allows silent signed integer overflow, then +Word.T is ok. No it's really not OK even if the implementation doesn't **happen to** check. I am not sure if it is spelled out anywhere, but it's clear to me that the intent of the language spec is that LAST(INTEGER) + 1 = LAST(Word.T) + 1 is a meaningless statement. The system is definitely **allowed to** treat it as a fatal error. It would be nice if it did, consistently. (BTW, other parts of the language spec ensure that the behavior resulting from not checking the range is not an "unchecked runtime error".) If you want it to wrap, use Word.Plus(LAST(Word.T),1)--that has the defined value FIRST(Word.T). But for example < is wrong, sometimes. Yes < is not really meaningful on integers MOD 2^N. Does Wednesday come before Sunday or does Sunday come before Wednesday? Both are obviously true. Of course there is "an order" on the integers MOD 2^N.. in fact there are (2^N)! ways of ordering them if all you want is "an order". There is an obvious one, but it's not usually that useful in the context of algorithms that ALSO care about wrapping the integers.... it's a bit of a trap actually, and I guess that's what you mean by "< is wrong, sometimes". I think a record containing an INTEGER, or array of bytes, would be better. Even better if the language prevented anything from reading/writing the fields, except for the implementation of the interface -- basically like C++ private:, or private + friend (if the functions are not member functions). Modula-3 seems to do plenty ok with the features of heap allocated garbage collected types, but much less good with value types and stack allocated and globals. I think that is a good approximate summary -- the features are all there, just not allowed on all types, esp. not on the "efficient" types. Yes, I think that's a good summary. (I also think importing FooRep is too easy, but I guess that can be limited to the library and not importers, because of lowercase interface and capital Interface in quake?) apparently cm3 doesn't enforce that (anymore)? > not a machine-oriented definition. Everything needs to be somewhat machine-oriented, in order to be machine-implemented. Sure, that's why we live without the range checks. They are inconvenient to implement on most hardware... - Jay ________________________________ From: Mika Nystrom > Sent: Monday, May 24, 2021 10:02 PM To: Jay K > Cc: m3devel > Subject: Re: [M3devel] Word.T rules vs. enforcement? I'm not sure what the problem is here, but hmm, no + is generally not correct on Word.T. Integer overflow on INTEGERs in Modula-3 is defined as being an error, which may or may not be caught by the implementation. Usually when you use Word.T, you don't want integer overflow to be an error but to wrap. Word.T is an interface that interprets bits of type Word.T (usually = INTEGER) in a specific way. Nothing else. INTEGER is really meant to look like Peano's numbers. It's a mathematics-oriented definition, not a machine-oriented definition. That's the Algol Way. If you want the machine-oriented view (where Word.T is an integer MOD 2^WordSize and all the operations are defined on such objects), use the facilities defined in the Word interface to manipulate those (same) bits. Mika P.S. I saw your emails about semaphores. I will get to it I promise---I need to try to remember what I did there before responding. On Mon, May 24, 2021 at 2:31 PM Jay K > wrote: Oops, actually + is correct, assuming integer overflow is well defined..which I believe we are unfortunately ambivalent about. :( - Jay ________________________________ From: M3devel > on behalf of Jay K > Sent: Monday, May 24, 2021 1:56:22 PM To: m3devel > Subject: [M3devel] Word.T rules vs. enforcement? This Word.T business seems bad to me. In that, it has plenty of rules, and no enforcement. I get that assignment is pass through, works. And compare for equality/inequality are ok. But you are never supposed to use e.g. "+" on Word.T. But yet, "+" does work on Word.T. It compiles. It is usually correct. But it could go wrong for overflow. And then "<" compiles, and works sometimes, and is wrong. Is there a better way? I'd suggest REFANY or traced pointer to branded record, but these have GC implications. And untraced pointer is unsafe. So stuck? How about a record containing an integer, passed around by value? That is still abusable but with more effort. It could even be an array of char to dissuade manual use, that the implementation could cast away. Of course, hoping/assuming the ABI deals efficiently with small records. And then I have to decide what type C should use for Word.T. I think it should actually be unsigned. - Jay _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://m3lists.elegosoft.com/mailman/listinfo/m3devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Tue May 25 19:03:16 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Tue, 25 May 2021 12:03:16 -0500 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: Message-ID: Modula-3's handling of full-range unsigned arithmetic has a fundamental difference from C and most other languages. I wrote a thing a few years ago about the language design pros and cons here. It's a case where you have two of three desirable properties, and Modula-3 chose differently, at least of the languages I am familiar with. I'll see if I can find the article. But language design issues aside, here is the way Modula-3 specifies, and what an implementation must, well, implement. In C and many others, there are different types for signed and unsigned full-range integers. Then the arithmetic operators, '+' are overloaded. An implementation must translate them to use different arithmetic, depending on the type of the operands, similar to integer vs. floating arithmetic. In Modula-3 instead, there are different, non-overloaded operators for the two kinds of arithmetic. '+', etc. always use signed arithmetic. 'Word.Plus' and cousins always use unsigned. Since the type is not used to distinguish which arithmetic, there are not two types. INTEGER=Word.T. They are just different names for the same type. Either group of operators can be applied to it. Since C's semantics are different, preserving Modula-3's in C has to look different. You have to use the choice of which kind of Modula-3 operator to case the operands to the appropriate signedness, so C's one overloaded operator will get translated by the C compiler. As an aside, as a kind of documentation, I always declare a variable INTEGER if its value is treated as signed and Word.T otherwise. This makes it a lot easier for me, at least, and hopefully any other readers, to keep track of things. But it's not linguistically different. Additionally, Modula-3 always does arithmetic of either kind in a full-range field, including intermediate expression results, even if the operand and/or result variables are are subranges. Thus, extension and truncation occur only on loads and stores. This hugely simplifies both language definition and code when dealing with mixed sizes. I can personally attest from much hard experience that writing overflow-tolerant code can be a nightmare when intermediate results get promoted to the larger of the operands or such rules. Note that this means that a subrange must fit either in a full-range signed or full-range unsigned field, but can't span them both. This a front-end static check. Subrange bounds are always interpreted as signed, has created some confusion in the past. But the signed 32-bit values and the unsigned 32-bit values all fit in a single 64-bit word, treated as signed, so a subrange can be either, with the bounds specified as signed constants. As for overflows, Word defines its operations to be modulo Word.t, treated as unsigned. Signed operators aren't defined WRT overflow. For calling handwritten C code from Modula-3, the C programmer will need some way of knowing whether a full-range int should be treated as signed or unsigned. I can't think of a way to provide static type checking of this, but that is nothing new across a language boundary. I guess the Modula-3 extern signature needs a comment and the C prototype can declare the signedness explicitly. On 5/25/21 12:19 AM, Jay K wrote: > Aside: > > Here is an example of the inconsistency btw: > > WebInfo.m3: import_procedure name:Word__Divide return_typename:INTEGER > Word.i3: ? ?import_procedure name:Word__Divide return_typename:Word__T > > I freely admit, return_typename is new, I added it. > So no preexisting code cares. > In both cases it resolves to signed INT64. > ? So even new code is pretty ok. > > But I kinda do need to fix it and it is kinda wrong/bad. > > The reason is: > ? - I want to concatenate all the m3c output. > ? - Including with hand written C. > ? ? ?Maybe this is misguided. But it'll enable redistributing > ? ? ?a minimal cm3 system as one large file. > ? ? ?It makes some sense on a per-library basis as well, for > ? ? ?"old fashioned LTCG". But incremental build suffers, and > ? ? ?it stresses the C compiler. > > So every m3c file should be consistent with other m3c files. > i.e. if they declare the same function, it should have the same signature. > > Ok, so typedef something INTEGER; typedef INTEGER Word__T; > > But also chosen interpretation of Word.T/Word__T should make sense for hand written C. > C functions should be able to take Word.T..well..maybe. Maybe no need. > > It isn't a big huge deal but it is something. > > I had actually introduced WORD_T (all caps, single underscore) for functions > taking Word.T, years ago. And for internal use, as being approximately > size_t or uintptr_t, which it matches on most systems (except VMS..) > > Perhaps having one type for Modula3 and another for C is an ok idea. > Signed for Modula3, unsigned for C. > But it was done by accident, and the names reflect that. > > I'm still not sure what to do, but there isn't an immediate problem. > I am still working through more m3c/C divergences. > > ? > (BTW, other parts of the language spec ensure that the behavior > ? > resulting from not checking the range is not an "unchecked runtime error".) > > Please avoid double/triple negatives. > I think you are saying, that even in the face of silent integer overflow, we will not suffer any thing else, like array overflow or double free. > > ?> Yes < is not really meaningful on integers MOD 2^N. > > Well, what I meant was, Word.T < does a signed comparison. > It is the intent that Word32.T{0x80000000} > Word32.T{0x7FFFFFFF}, but it is not. > It only works if you call Word.LT. > > I understand your point, that in the face of silent rollover you also get the wrong result. > This is the contract of Word.T and the ambivalent contract of INTEGER. > > Relevant aside: I think only Rust has really tried to solve this, with the obvious semi-complicated outcome: You need a bunch of types and let the user decide on a case by case basis. But hopefully good defaults. > > ?> They are inconvenient to implement on most hardware... > > Not at all, "just" inefficient if portable, or a few machine specific nonportable options. > Too much to write here and now. > > ?> Yes, I think that's a good summary. > > Can/should we fix/change it? > Like, should I be able to make an opaque type, that has a known constant size and is "blittable" (memcpy-able, maybe memcmp-able)? > Are small records enough? > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Mika Nystrom > *Sent:* Monday, May 24, 2021 11:18 PM > *To:* Jay K > *Cc:* m3devel > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > > > On Mon, May 24, 2021 at 3:27 PM Jay K > wrote: > > I am not criticizing the functions and their meaning in the Word interface per se. > > My problem is that Word.T, the type, is not actually limited > by the language/interface, to be used only with the Word interface functions. > > Its INTEGER-ness is transparent. > > That seems bad. > > + is as you say, "in between". > If the runtime traps signed integer overflow, then +Word.T is wrong. > If the runtime allows silent signed integer overflow, then +Word.T is ok. > > > No it's really not OK even if the implementation doesn't **happen to** check. > > I am not sure if it is spelled out anywhere, but it's clear to me that the intent of the language spec is that > > LAST(INTEGER)?+ 1 = LAST(Word.T)?+ 1 > > is a meaningless statement.? The system is definitely **allowed to** treat it as a fatal error.? It would be nice if it did, consistently. > > (BTW, other parts of the language spec ensure that the behavior resulting from not checking the range is not an "unchecked runtime error".) > > If you want it to wrap, use Word.Plus(LAST(Word.T),1)--that has the defined value FIRST(Word.T). > > But for example < is wrong, sometimes. > > > Yes < is not really meaningful on integers MOD 2^N.? Does Wednesday come before Sunday or does Sunday come before Wednesday?? Both are obviously true. > > Of course there is "an order" on the integers MOD 2^N.. in fact there are (2^N)! ways of ordering them if all you want is "an order".? There is an obvious one, but it's not usually that useful in the context of algorithms that ALSO care about wrapping the integers.... it's a bit of a trap actually, and I guess that's what you mean by "< is wrong, sometimes". > > > I think a record containing an INTEGER, or array of bytes, would be better. > Even better if the language prevented anything from reading/writing the fields, > except for the implementation of the interface -- basically like C++ private:, > or private + friend (if the functions are not member functions). > > Modula-3 seems to do plenty ok with the features of heap allocated garbage > collected types, but much less good with value types and stack allocated and > globals. I think that is a good approximate summary -- the features are all there, > just not allowed on all types, esp. not on the "efficient" types. > > > Yes, I think that's a good summary. > > > (I also think importing FooRep is too easy, but I guess that can be limited to the library > and not importers, because of lowercase interface and capital Interface in quake?) > > > apparently cm3 doesn't enforce that (anymore)? > > > >not a machine-oriented definition. > > Everything needs to be somewhat machine-oriented, in order to be machine-implemented. > > > Sure, that's why we live without the range checks.? They are inconvenient to implement on most hardware... > > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Mika Nystrom > > *Sent:* Monday, May 24, 2021 10:02 PM > *To:* Jay K > > *Cc:* m3devel > > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > I'm not sure what the problem is here, but hmm, no?+ is generally not correct on Word.T. > > Integer overflow on INTEGERs in Modula-3 is defined as being an error, which may or may not be caught by the implementation. > > Usually when you use Word.T, you don't want integer overflow to be an error but to wrap. > > Word.T is an interface that interprets bits of type Word.T (usually = INTEGER) in a specific way.? Nothing else. > > INTEGER is really meant to look like Peano's numbers.? It's a mathematics-oriented definition, not a machine-oriented definition.? That's the Algol Way. > > If you want the machine-oriented view (where Word.T is an integer MOD 2^WordSize and all the operations are defined on such objects), use the facilities defined in the Word interface to manipulate those (same) bits. > > ? ? ?Mika > > P.S. I saw your emails about semaphores.? I will get to it I promise---I need to try to remember what I did there before responding. > > On Mon, May 24, 2021 at 2:31 PM Jay K > wrote: > > Oops, actually + is correct, assuming integer overflow is well defined..which I believe we are unfortunately ambivalent about. :( > > - Jay > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* M3devel > on behalf of Jay K > > *Sent:* Monday, May 24, 2021 1:56:22 PM > *To:* m3devel > > *Subject:* [M3devel] Word.T rules vs. enforcement? > This Word.T business seems bad to me. > > In that, it has plenty of rules, and no enforcement. > > I get that assignment is pass through, works. > And compare for equality/inequality are ok. > > But you are never supposed to use e.g. "+" on Word.T. > But yet, "+" does work on Word.T. It compiles. It is usually > correct. But it could go wrong for overflow. > And then "<" compiles, and works sometimes, and is wrong. > > Is there a better way? > > I'd suggest REFANY or traced pointer to branded record, but these have GC implications. > > And untraced pointer is unsafe. > > So stuck? > > How about a record containing an integer, passed around by value? > That is still abusable but with more effort. > It could even be an array of char to dissuade manual use, that the implementation could cast away. > Of course, hoping/assuming the ABI deals efficiently with small records. > > And then I have to decide what type C should use for Word.T. > I think it should actually be unsigned. > > ?- Jay > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Wed May 26 09:33:01 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 26 May 2021 07:33:01 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: , Message-ID: I think I understand this pretty well. My initial complaint, is I want this incorrect code to fail to compile: PROCEDURE BadAdd(a,b:Word.T):Word.T= BEGIN RETURN a + b; END BadAdd; PROCEDURE BadLT(a,b:Word.T):Word.T= BEGIN RETURN a < b; END BadLT; The fact that Word.T is INTEGER should not be visible to code outside of interface Word. They should be forced to call into Word functions. Very much like C++ private or Modula-3 opaque types (which unfortunately must be pointers). The stated rules are ok. The problem is the compiler cannot enforce them. It might help to say: TYPE Word.T = RECORD opaque:INTEGER END; I realize it is an arms race. I could still write: PROCEDURE BadAdd(a,b:Word.T):Word.T= BEGIN RETURN Word.T{a.opaque + b.opaque}; END BadAdd; but at least not by accident (unless via copy/paste..) I also am not sure what to do.. Given an external C function declared as taking Word.T what should the C function think its parameters are? Signed or unsigned? Worse given an external C function returning Word.T, what to do? (M3front does/should issue range checks for return values of external functions? It maybe should.) I admit I got confused. I was confusing Word.T with CARDINAL. CARDINAL is half range, 0..LAST(INTEGER). Word.T is full range. That is, C code more often takes Word.T than CARDINAL. So the answer is pretty clearly it should be full range unsigned. C code taking CARDINAL begs similar but more difficult question. Maybe there is no such code and there shall be none. And then I was reminded, I also want a bunch more integer types/functions. For legacy reasons, signed and unsigned with silent overflow are needed. But then there should be some sort of "trapping" types. Where "trapping" could be defined in a few ways: BOOLEAN return: TRUE for trap BOOLEAN return: TRUE for "success", FALSE for "failure" raise exception an out BOOLEAN parameter named overflow an out INTEGER parmeter named carry (i.e. building block for multiple precision math) For multiplication, an out INTEGER parameter named highPart or something. For division, I am not sure, I hadn't considered it, but some "checked integer" libraries handle it. I realize at some point you want rational numbers, multiple precision, and automatic dynamic conversion to a representation that best represents the value, including possible "an expression" with deferred evaluation. - Jay ________________________________ From: Rodney M. Bates Sent: Tuesday, May 25, 2021 5:03 PM To: Jay K ; Mika Nystrom Cc: m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? Modula-3's handling of full-range unsigned arithmetic has a fundamental difference from C and most other languages. I wrote a thing a few years ago about the language design pros and cons here. It's a case where you have two of three desirable properties, and Modula-3 chose differently, at least of the languages I am familiar with. I'll see if I can find the article. But language design issues aside, here is the way Modula-3 specifies, and what an implementation must, well, implement. In C and many others, there are different types for signed and unsigned full-range integers. Then the arithmetic operators, '+' are overloaded. An implementation must translate them to use different arithmetic, depending on the type of the operands, similar to integer vs. floating arithmetic. In Modula-3 instead, there are different, non-overloaded operators for the two kinds of arithmetic. '+', etc. always use signed arithmetic. 'Word.Plus' and cousins always use unsigned. Since the type is not used to distinguish which arithmetic, there are not two types. INTEGER=Word.T. They are just different names for the same type. Either group of operators can be applied to it. Since C's semantics are different, preserving Modula-3's in C has to look different. You have to use the choice of which kind of Modula-3 operator to case the operands to the appropriate signedness, so C's one overloaded operator will get translated by the C compiler. As an aside, as a kind of documentation, I always declare a variable INTEGER if its value is treated as signed and Word.T otherwise. This makes it a lot easier for me, at least, and hopefully any other readers, to keep track of things. But it's not linguistically different. Additionally, Modula-3 always does arithmetic of either kind in a full-range field, including intermediate expression results, even if the operand and/or result variables are are subranges. Thus, extension and truncation occur only on loads and stores. This hugely simplifies both language definition and code when dealing with mixed sizes. I can personally attest from much hard experience that writing overflow-tolerant code can be a nightmare when intermediate results get promoted to the larger of the operands or such rules. Note that this means that a subrange must fit either in a full-range signed or full-range unsigned field, but can't span them both. This a front-end static check. Subrange bounds are always interpreted as signed, has created some confusion in the past. But the signed 32-bit values and the unsigned 32-bit values all fit in a single 64-bit word, treated as signed, so a subrange can be either, with the bounds specified as signed constants. As for overflows, Word defines its operations to be modulo Word.t, treated as unsigned. Signed operators aren't defined WRT overflow. For calling handwritten C code from Modula-3, the C programmer will need some way of knowing whether a full-range int should be treated as signed or unsigned. I can't think of a way to provide static type checking of this, but that is nothing new across a language boundary. I guess the Modula-3 extern signature needs a comment and the C prototype can declare the signedness explicitly. On 5/25/21 12:19 AM, Jay K wrote: > Aside: > > Here is an example of the inconsistency btw: > > WebInfo.m3: import_procedure name:Word__Divide return_typename:INTEGER > Word.i3: import_procedure name:Word__Divide return_typename:Word__T > > I freely admit, return_typename is new, I added it. > So no preexisting code cares. > In both cases it resolves to signed INT64. > So even new code is pretty ok. > > But I kinda do need to fix it and it is kinda wrong/bad. > > The reason is: > - I want to concatenate all the m3c output. > - Including with hand written C. > Maybe this is misguided. But it'll enable redistributing > a minimal cm3 system as one large file. > It makes some sense on a per-library basis as well, for > "old fashioned LTCG". But incremental build suffers, and > it stresses the C compiler. > > So every m3c file should be consistent with other m3c files. > i.e. if they declare the same function, it should have the same signature. > > Ok, so typedef something INTEGER; typedef INTEGER Word__T; > > But also chosen interpretation of Word.T/Word__T should make sense for hand written C. > C functions should be able to take Word.T..well..maybe. Maybe no need. > > It isn't a big huge deal but it is something. > > I had actually introduced WORD_T (all caps, single underscore) for functions > taking Word.T, years ago. And for internal use, as being approximately > size_t or uintptr_t, which it matches on most systems (except VMS..) > > Perhaps having one type for Modula3 and another for C is an ok idea. > Signed for Modula3, unsigned for C. > But it was done by accident, and the names reflect that. > > I'm still not sure what to do, but there isn't an immediate problem. > I am still working through more m3c/C divergences. > > > (BTW, other parts of the language spec ensure that the behavior > > resulting from not checking the range is not an "unchecked runtime error".) > > Please avoid double/triple negatives. > I think you are saying, that even in the face of silent integer overflow, we will not suffer any thing else, like array overflow or double free. > > > Yes < is not really meaningful on integers MOD 2^N. > > Well, what I meant was, Word.T < does a signed comparison. > It is the intent that Word32.T{0x80000000} > Word32.T{0x7FFFFFFF}, but it is not. > It only works if you call Word.LT. > > I understand your point, that in the face of silent rollover you also get the wrong result. > This is the contract of Word.T and the ambivalent contract of INTEGER. > > Relevant aside: I think only Rust has really tried to solve this, with the obvious semi-complicated outcome: You need a bunch of types and let the user decide on a case by case basis. But hopefully good defaults. > > > They are inconvenient to implement on most hardware... > > Not at all, "just" inefficient if portable, or a few machine specific nonportable options. > Too much to write here and now. > > > Yes, I think that's a good summary. > > Can/should we fix/change it? > Like, should I be able to make an opaque type, that has a known constant size and is "blittable" (memcpy-able, maybe memcmp-able)? > Are small records enough? > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Mika Nystrom > *Sent:* Monday, May 24, 2021 11:18 PM > *To:* Jay K > *Cc:* m3devel > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > > > On Mon, May 24, 2021 at 3:27 PM Jay K > wrote: > > I am not criticizing the functions and their meaning in the Word interface per se. > > My problem is that Word.T, the type, is not actually limited > by the language/interface, to be used only with the Word interface functions. > > Its INTEGER-ness is transparent. > > That seems bad. > > + is as you say, "in between". > If the runtime traps signed integer overflow, then +Word.T is wrong. > If the runtime allows silent signed integer overflow, then +Word.T is ok. > > > No it's really not OK even if the implementation doesn't **happen to** check. > > I am not sure if it is spelled out anywhere, but it's clear to me that the intent of the language spec is that > > LAST(INTEGER) + 1 = LAST(Word.T) + 1 > > is a meaningless statement. The system is definitely **allowed to** treat it as a fatal error. It would be nice if it did, consistently. > > (BTW, other parts of the language spec ensure that the behavior resulting from not checking the range is not an "unchecked runtime error".) > > If you want it to wrap, use Word.Plus(LAST(Word.T),1)--that has the defined value FIRST(Word.T). > > But for example < is wrong, sometimes. > > > Yes < is not really meaningful on integers MOD 2^N. Does Wednesday come before Sunday or does Sunday come before Wednesday? Both are obviously true. > > Of course there is "an order" on the integers MOD 2^N.. in fact there are (2^N)! ways of ordering them if all you want is "an order". There is an obvious one, but it's not usually that useful in the context of algorithms that ALSO care about wrapping the integers.... it's a bit of a trap actually, and I guess that's what you mean by "< is wrong, sometimes". > > > I think a record containing an INTEGER, or array of bytes, would be better. > Even better if the language prevented anything from reading/writing the fields, > except for the implementation of the interface -- basically like C++ private:, > or private + friend (if the functions are not member functions). > > Modula-3 seems to do plenty ok with the features of heap allocated garbage > collected types, but much less good with value types and stack allocated and > globals. I think that is a good approximate summary -- the features are all there, > just not allowed on all types, esp. not on the "efficient" types. > > > Yes, I think that's a good summary. > > > (I also think importing FooRep is too easy, but I guess that can be limited to the library > and not importers, because of lowercase interface and capital Interface in quake?) > > > apparently cm3 doesn't enforce that (anymore)? > > > >not a machine-oriented definition. > > Everything needs to be somewhat machine-oriented, in order to be machine-implemented. > > > Sure, that's why we live without the range checks. They are inconvenient to implement on most hardware... > > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Mika Nystrom > > *Sent:* Monday, May 24, 2021 10:02 PM > *To:* Jay K > > *Cc:* m3devel > > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > I'm not sure what the problem is here, but hmm, no + is generally not correct on Word.T. > > Integer overflow on INTEGERs in Modula-3 is defined as being an error, which may or may not be caught by the implementation. > > Usually when you use Word.T, you don't want integer overflow to be an error but to wrap. > > Word.T is an interface that interprets bits of type Word.T (usually = INTEGER) in a specific way. Nothing else. > > INTEGER is really meant to look like Peano's numbers. It's a mathematics-oriented definition, not a machine-oriented definition. That's the Algol Way. > > If you want the machine-oriented view (where Word.T is an integer MOD 2^WordSize and all the operations are defined on such objects), use the facilities defined in the Word interface to manipulate those (same) bits. > > Mika > > P.S. I saw your emails about semaphores. I will get to it I promise---I need to try to remember what I did there before responding. > > On Mon, May 24, 2021 at 2:31 PM Jay K > wrote: > > Oops, actually + is correct, assuming integer overflow is well defined..which I believe we are unfortunately ambivalent about. :( > > - Jay > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* M3devel > on behalf of Jay K > > *Sent:* Monday, May 24, 2021 1:56:22 PM > *To:* m3devel > > *Subject:* [M3devel] Word.T rules vs. enforcement? > This Word.T business seems bad to me. > > In that, it has plenty of rules, and no enforcement. > > I get that assignment is pass through, works. > And compare for equality/inequality are ok. > > But you are never supposed to use e.g. "+" on Word.T. > But yet, "+" does work on Word.T. It compiles. It is usually > correct. But it could go wrong for overflow. > And then "<" compiles, and works sometimes, and is wrong. > > Is there a better way? > > I'd suggest REFANY or traced pointer to branded record, but these have GC implications. > > And untraced pointer is unsafe. > > So stuck? > > How about a record containing an integer, passed around by value? > That is still abusable but with more effort. > It could even be an array of char to dissuade manual use, that the implementation could cast away. > Of course, hoping/assuming the ABI deals efficiently with small records. > > And then I have to decide what type C should use for Word.T. > I think it should actually be unsigned. > > - Jay > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C81a7cce0eaee47372b4108d91f9f083f%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637575590164056044%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=Cf1%2BSJP8lRGwpojkoJ10bhg79gEpbYMvgTznYzdBSQM%3D&reserved=0 > > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C81a7cce0eaee47372b4108d91f9f083f%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637575590164056044%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=Cf1%2BSJP8lRGwpojkoJ10bhg79gEpbYMvgTznYzdBSQM%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Wed May 26 23:09:52 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 26 May 2021 21:09:52 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: , Message-ID: Huh? A type should really only have one meaning. To make a new meaning, make a new type. Maybe you mean, Word.T imbues an opaque/untyped block of 4 or 8 bytes with its own meaning? I think I don't really want C or C++, because I want safety. But otherwise they are quite familiar and "expressive". Esp. C++. The familiarity is hard to get past. But the missing safety seems clearly bad. You can see that opaque or partially opaque types are widely desired and widely provided, in Modula-3 and C. C++ does it trivially with private:. In C there are a few attempts: opaque int. So you can compile this nonsense: int fd1 = open(..); int fd2 = open(...); close(fd1 + fd2); The compiler does not help. This is the same problem as Word.T. opaque pointer to undefined struct: typedef struct _FOO *FOO; typedef struct _BAR *BAR; FOO foo = OpenFoo(); CloseFoo(foo); FOO bar = CreateBar(); DrawBar(bar); This is almost ideal, but has problems. - Limited to a pointer's worth of bytes, or heap allocate, or cast to integer and index an array. - No good way to describe related types. User casts. void* because of related types and common functions, Windows: HANDLE file = CreateFile(); HANDLE event = CreateEvent(); HANDLE handles[] = {file, event}; WaitForMultipleObjects(handles, 2); CloseHandle(event); CloseHandle(file); but then you can compile nonsense like: ReadFile(event); memcpy(file); file = event; >From a Modula-3 point of view, things are a bit better, because you can have a public type hierarchy among opaque types. But you still have the problem that it only works for pointers. --- I agree the two level hierarchy struck me as obviously not general. I try to ignore it. Can we fix it? Or just make longer names? Is there really a difference between A.B.T and AB.T? Would you say IMPORT B FROM A; B.T? a Is that the difference? Hierarchical names provide more avenues for abbreviations? You don't want to say FROM AB IMPORT T, too short? nor FROM AB IMPORT T AS BT? (I think that is the syntax) or TYPE BT = AB.T? (Definitely the syntax) because, perhaps, tedium, and a less good plain text search outcome? i.e. search for "canonical suffix" B.T? I do hope that either we can decide these are not actually improvements, or we can grow/change the language. And not feel stuck. - Jay ________________________________ From: Mika Nystrom Sent: Wednesday, May 26, 2021 6:16 PM To: Jay K Cc: rodney.m.bates at acm.org ; m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? Jay I think you're trying to squeeze Modula-3 into C. Word is not a special type. It's an interface that offers a special interpretation of INTEGER. M3 and C are different languages, and you really can't expect them to have the same semantics. M3 has its own semantics, don't try to turn it into C. Is interop with C important? Sure it is, there is so much software with C APIs. But most M3 code only talks to other M3 code. C interfaces tend to be narrow and specially tailored. There are so many other issues with them (in particular figuring out how to subject C libraries to GC) that it seems to me that worrying about how to represent "unsigned ints" in Modula-3 is really kind of an esoteric issue. That being said, sure it would be nice to have opaque RECORDs. But it's another one of the things that used to bother me about M3 that no longer does. The thing I think is the worst issue with M3 today is how the flat package system makes it far too easy to make name-clashing modules. Mika On Wed, May 26, 2021 at 12:33 AM Jay K > wrote: I think I understand this pretty well. My initial complaint, is I want this incorrect code to fail to compile: PROCEDURE BadAdd(a,b:Word.T):Word.T= BEGIN RETURN a + b; END BadAdd; PROCEDURE BadLT(a,b:Word.T):Word.T= BEGIN RETURN a < b; END BadLT; The fact that Word.T is INTEGER should not be visible to code outside of interface Word. They should be forced to call into Word functions. Very much like C++ private or Modula-3 opaque types (which unfortunately must be pointers). The stated rules are ok. The problem is the compiler cannot enforce them. It might help to say: TYPE Word.T = RECORD opaque:INTEGER END; I realize it is an arms race. I could still write: PROCEDURE BadAdd(a,b:Word.T):Word.T= BEGIN RETURN Word.T{a.opaque + b.opaque}; END BadAdd; but at least not by accident (unless via copy/paste..) I also am not sure what to do.. Given an external C function declared as taking Word.T what should the C function think its parameters are? Signed or unsigned? Worse given an external C function returning Word.T, what to do? (M3front does/should issue range checks for return values of external functions? It maybe should.) I admit I got confused. I was confusing Word.T with CARDINAL. CARDINAL is half range, 0..LAST(INTEGER). Word.T is full range. That is, C code more often takes Word.T than CARDINAL. So the answer is pretty clearly it should be full range unsigned. C code taking CARDINAL begs similar but more difficult question. Maybe there is no such code and there shall be none. And then I was reminded, I also want a bunch more integer types/functions. For legacy reasons, signed and unsigned with silent overflow are needed. But then there should be some sort of "trapping" types. Where "trapping" could be defined in a few ways: BOOLEAN return: TRUE for trap BOOLEAN return: TRUE for "success", FALSE for "failure" raise exception an out BOOLEAN parameter named overflow an out INTEGER parmeter named carry (i.e. building block for multiple precision math) For multiplication, an out INTEGER parameter named highPart or something. For division, I am not sure, I hadn't considered it, but some "checked integer" libraries handle it. I realize at some point you want rational numbers, multiple precision, and automatic dynamic conversion to a representation that best represents the value, including possible "an expression" with deferred evaluation. - Jay ________________________________ From: Rodney M. Bates > Sent: Tuesday, May 25, 2021 5:03 PM To: Jay K >; Mika Nystrom > Cc: m3devel > Subject: Re: [M3devel] Word.T rules vs. enforcement? Modula-3's handling of full-range unsigned arithmetic has a fundamental difference from C and most other languages. I wrote a thing a few years ago about the language design pros and cons here. It's a case where you have two of three desirable properties, and Modula-3 chose differently, at least of the languages I am familiar with. I'll see if I can find the article. But language design issues aside, here is the way Modula-3 specifies, and what an implementation must, well, implement. In C and many others, there are different types for signed and unsigned full-range integers. Then the arithmetic operators, '+' are overloaded. An implementation must translate them to use different arithmetic, depending on the type of the operands, similar to integer vs. floating arithmetic. In Modula-3 instead, there are different, non-overloaded operators for the two kinds of arithmetic. '+', etc. always use signed arithmetic. 'Word.Plus' and cousins always use unsigned. Since the type is not used to distinguish which arithmetic, there are not two types. INTEGER=Word.T. They are just different names for the same type. Either group of operators can be applied to it. Since C's semantics are different, preserving Modula-3's in C has to look different. You have to use the choice of which kind of Modula-3 operator to case the operands to the appropriate signedness, so C's one overloaded operator will get translated by the C compiler. As an aside, as a kind of documentation, I always declare a variable INTEGER if its value is treated as signed and Word.T otherwise. This makes it a lot easier for me, at least, and hopefully any other readers, to keep track of things. But it's not linguistically different. Additionally, Modula-3 always does arithmetic of either kind in a full-range field, including intermediate expression results, even if the operand and/or result variables are are subranges. Thus, extension and truncation occur only on loads and stores. This hugely simplifies both language definition and code when dealing with mixed sizes. I can personally attest from much hard experience that writing overflow-tolerant code can be a nightmare when intermediate results get promoted to the larger of the operands or such rules. Note that this means that a subrange must fit either in a full-range signed or full-range unsigned field, but can't span them both. This a front-end static check. Subrange bounds are always interpreted as signed, has created some confusion in the past. But the signed 32-bit values and the unsigned 32-bit values all fit in a single 64-bit word, treated as signed, so a subrange can be either, with the bounds specified as signed constants. As for overflows, Word defines its operations to be modulo Word.t, treated as unsigned. Signed operators aren't defined WRT overflow. For calling handwritten C code from Modula-3, the C programmer will need some way of knowing whether a full-range int should be treated as signed or unsigned. I can't think of a way to provide static type checking of this, but that is nothing new across a language boundary. I guess the Modula-3 extern signature needs a comment and the C prototype can declare the signedness explicitly. On 5/25/21 12:19 AM, Jay K wrote: > Aside: > > Here is an example of the inconsistency btw: > > WebInfo.m3: import_procedure name:Word__Divide return_typename:INTEGER > Word.i3: import_procedure name:Word__Divide return_typename:Word__T > > I freely admit, return_typename is new, I added it. > So no preexisting code cares. > In both cases it resolves to signed INT64. > So even new code is pretty ok. > > But I kinda do need to fix it and it is kinda wrong/bad. > > The reason is: > - I want to concatenate all the m3c output. > - Including with hand written C. > Maybe this is misguided. But it'll enable redistributing > a minimal cm3 system as one large file. > It makes some sense on a per-library basis as well, for > "old fashioned LTCG". But incremental build suffers, and > it stresses the C compiler. > > So every m3c file should be consistent with other m3c files. > i.e. if they declare the same function, it should have the same signature. > > Ok, so typedef something INTEGER; typedef INTEGER Word__T; > > But also chosen interpretation of Word.T/Word__T should make sense for hand written C. > C functions should be able to take Word.T..well..maybe. Maybe no need. > > It isn't a big huge deal but it is something. > > I had actually introduced WORD_T (all caps, single underscore) for functions > taking Word.T, years ago. And for internal use, as being approximately > size_t or uintptr_t, which it matches on most systems (except VMS..) > > Perhaps having one type for Modula3 and another for C is an ok idea. > Signed for Modula3, unsigned for C. > But it was done by accident, and the names reflect that. > > I'm still not sure what to do, but there isn't an immediate problem. > I am still working through more m3c/C divergences. > > > (BTW, other parts of the language spec ensure that the behavior > > resulting from not checking the range is not an "unchecked runtime error".) > > Please avoid double/triple negatives. > I think you are saying, that even in the face of silent integer overflow, we will not suffer any thing else, like array overflow or double free. > > > Yes < is not really meaningful on integers MOD 2^N. > > Well, what I meant was, Word.T < does a signed comparison. > It is the intent that Word32.T{0x80000000} > Word32.T{0x7FFFFFFF}, but it is not. > It only works if you call Word.LT. > > I understand your point, that in the face of silent rollover you also get the wrong result. > This is the contract of Word.T and the ambivalent contract of INTEGER. > > Relevant aside: I think only Rust has really tried to solve this, with the obvious semi-complicated outcome: You need a bunch of types and let the user decide on a case by case basis. But hopefully good defaults. > > > They are inconvenient to implement on most hardware... > > Not at all, "just" inefficient if portable, or a few machine specific nonportable options. > Too much to write here and now. > > > Yes, I think that's a good summary. > > Can/should we fix/change it? > Like, should I be able to make an opaque type, that has a known constant size and is "blittable" (memcpy-able, maybe memcmp-able)? > Are small records enough? > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Mika Nystrom > > *Sent:* Monday, May 24, 2021 11:18 PM > *To:* Jay K > > *Cc:* m3devel > > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > > > On Mon, May 24, 2021 at 3:27 PM Jay K > wrote: > > I am not criticizing the functions and their meaning in the Word interface per se. > > My problem is that Word.T, the type, is not actually limited > by the language/interface, to be used only with the Word interface functions. > > Its INTEGER-ness is transparent. > > That seems bad. > > + is as you say, "in between". > If the runtime traps signed integer overflow, then +Word.T is wrong. > If the runtime allows silent signed integer overflow, then +Word.T is ok. > > > No it's really not OK even if the implementation doesn't **happen to** check. > > I am not sure if it is spelled out anywhere, but it's clear to me that the intent of the language spec is that > > LAST(INTEGER) + 1 = LAST(Word.T) + 1 > > is a meaningless statement. The system is definitely **allowed to** treat it as a fatal error. It would be nice if it did, consistently. > > (BTW, other parts of the language spec ensure that the behavior resulting from not checking the range is not an "unchecked runtime error".) > > If you want it to wrap, use Word.Plus(LAST(Word.T),1)--that has the defined value FIRST(Word.T). > > But for example < is wrong, sometimes. > > > Yes < is not really meaningful on integers MOD 2^N. Does Wednesday come before Sunday or does Sunday come before Wednesday? Both are obviously true. > > Of course there is "an order" on the integers MOD 2^N.. in fact there are (2^N)! ways of ordering them if all you want is "an order". There is an obvious one, but it's not usually that useful in the context of algorithms that ALSO care about wrapping the integers.... it's a bit of a trap actually, and I guess that's what you mean by "< is wrong, sometimes". > > > I think a record containing an INTEGER, or array of bytes, would be better. > Even better if the language prevented anything from reading/writing the fields, > except for the implementation of the interface -- basically like C++ private:, > or private + friend (if the functions are not member functions). > > Modula-3 seems to do plenty ok with the features of heap allocated garbage > collected types, but much less good with value types and stack allocated and > globals. I think that is a good approximate summary -- the features are all there, > just not allowed on all types, esp. not on the "efficient" types. > > > Yes, I think that's a good summary. > > > (I also think importing FooRep is too easy, but I guess that can be limited to the library > and not importers, because of lowercase interface and capital Interface in quake?) > > > apparently cm3 doesn't enforce that (anymore)? > > > >not a machine-oriented definition. > > Everything needs to be somewhat machine-oriented, in order to be machine-implemented. > > > Sure, that's why we live without the range checks. They are inconvenient to implement on most hardware... > > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Mika Nystrom > > *Sent:* Monday, May 24, 2021 10:02 PM > *To:* Jay K > > *Cc:* m3devel > > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > I'm not sure what the problem is here, but hmm, no + is generally not correct on Word.T. > > Integer overflow on INTEGERs in Modula-3 is defined as being an error, which may or may not be caught by the implementation. > > Usually when you use Word.T, you don't want integer overflow to be an error but to wrap. > > Word.T is an interface that interprets bits of type Word.T (usually = INTEGER) in a specific way. Nothing else. > > INTEGER is really meant to look like Peano's numbers. It's a mathematics-oriented definition, not a machine-oriented definition. That's the Algol Way. > > If you want the machine-oriented view (where Word.T is an integer MOD 2^WordSize and all the operations are defined on such objects), use the facilities defined in the Word interface to manipulate those (same) bits. > > Mika > > P.S. I saw your emails about semaphores. I will get to it I promise---I need to try to remember what I did there before responding. > > On Mon, May 24, 2021 at 2:31 PM Jay K > wrote: > > Oops, actually + is correct, assuming integer overflow is well defined..which I believe we are unfortunately ambivalent about. :( > > - Jay > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* M3devel > on behalf of Jay K > > *Sent:* Monday, May 24, 2021 1:56:22 PM > *To:* m3devel > > *Subject:* [M3devel] Word.T rules vs. enforcement? > This Word.T business seems bad to me. > > In that, it has plenty of rules, and no enforcement. > > I get that assignment is pass through, works. > And compare for equality/inequality are ok. > > But you are never supposed to use e.g. "+" on Word.T. > But yet, "+" does work on Word.T. It compiles. It is usually > correct. But it could go wrong for overflow. > And then "<" compiles, and works sometimes, and is wrong. > > Is there a better way? > > I'd suggest REFANY or traced pointer to branded record, but these have GC implications. > > And untraced pointer is unsafe. > > So stuck? > > How about a record containing an integer, passed around by value? > That is still abusable but with more effort. > It could even be an array of char to dissuade manual use, that the implementation could cast away. > Of course, hoping/assuming the ABI deals efficiently with small records. > > And then I have to decide what type C should use for Word.T. > I think it should actually be unsigned. > > - Jay > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C81a7cce0eaee47372b4108d91f9f083f%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637575590164056044%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=Cf1%2BSJP8lRGwpojkoJ10bhg79gEpbYMvgTznYzdBSQM%3D&reserved=0 > > > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C81a7cce0eaee47372b4108d91f9f083f%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637575590164056044%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=Cf1%2BSJP8lRGwpojkoJ10bhg79gEpbYMvgTznYzdBSQM%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Wed May 26 23:40:20 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Wed, 26 May 2021 16:40:20 -0500 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: Message-ID: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> No, you don't understand. You are trying to apply C semantics to Modula-3 On 5/26/21 2:33 AM, Jay K wrote: > I think I understand this pretty well. > > My initial complaint, is I want this incorrect code to fail to compile: > > PROCEDURE BadAdd(a,b:Word.T):Word.T= > BEGIN > ? RETURN a + b; > END BadAdd; > > PROCEDURE BadLT(a,b:Word.T):Word.T= > BEGIN > ? RETURN a < b; > END BadLT; These examples are correct, though not, IMO, the best style. They would be equivalent if Word.T were replaced by INTEGER. They would be also correct but different if '+' were replaced by Word.Plus. The determination of whether arithmetic is signed or unsigned comes from the operator: '+' or Word.Plus, not the type. It couldn't come from the type, because Word.T and INTEGER are just different names for the same type. Choosing which to declare something as is only equivalent to a comment, though I would consider it a good one. > > The fact that Word.T is INTEGER should not be visible to code outside of interface Word. > They should be forced to call into Word functions. > > Very much like C++ private or Modula-3 opaque types (which unfortunately must be pointers). > > The stated rules are ok. The problem is the compiler cannot enforce them. > > It might help to say: > > TYPE Word.T = RECORD opaque:INTEGER END; > > I realize it is an arms race. > I could still write: > > PROCEDURE BadAdd(a,b:Word.T):Word.T= > BEGIN > ? RETURN Word.T{a.opaque + b.opaque}; > END BadAdd; > > but at least not by accident (unless via copy/paste..) > > I also am not sure what to do.. > > Given an external C function declared as taking Word.T > what should the C function think its parameters are? > Signed or unsigned? If the C function is a translation of Module-3 code, it can declare it as either, but wherever the C code contains a translation of a Modula-3 operator, it must cast the operands to the signedness implied by the Modula-3 operator, if the C operand doesn't already have that signedness. If the C is handwritten, the writers of the two will have to agree on signedness outside the semantic rules of the two languages, which are different. But a nice practical convention would be if all Modula-3 programmers used INTEGER when signed interpretation of the operands is correct and Word.T when unsigned. Then a Modula-3 extern declaration for handwritten C could be mechanically translated to a C prototype, using this convention to determine the declared signedness of the C formal. Calling handwritten C from m3-to-C translated could follow the same convention. > > Worse given an external C function returning Word.T, what to do? > (M3front does/should issue range checks for return values of external functions? It maybe should.) If the type is INTEGER (or Word.T, same thing), then every bit pattern is a legal member of the type, so no range check could ever fail. If the result type is a subrange, then a check would be good. But remember, there are no full-range unsigned subranges. > > I admit I got confused. I was confusing Word.T with CARDINAL. > CARDINAL is half range, 0..LAST(INTEGER). Word.T is full range. > > That is, C code more often takes Word.T than CARDINAL. > So the answer is pretty clearly it should be full range unsigned. > > C code taking CARDINAL begs similar but more difficult question. > Maybe there is no such code and there shall be none. > > And then I was reminded, I also want a bunch more integer types/functions. > > For legacy reasons, signed and unsigned with silent overflow are needed. > But then there should be some sort of "trapping" types. > Where "trapping" could be defined in a few ways: > BOOLEAN return: TRUE for trap > BOOLEAN return: TRUE for "success", FALSE for "failure" > raise exception > an out BOOLEAN parameter named overflow > an out INTEGER parmeter named carry (i.e. building block for multiple precision math) > > For multiplication, an out INTEGER parameter named highPart or something. > For division, I am not sure, I hadn't considered it, but some "checked integer" libraries handle it. > > I realize at some point you want rational numbers, multiple precision, and automatic dynamic > conversion to a representation that best represents the value, including possible "an expression" > with deferred evaluation. > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Rodney M. Bates > *Sent:* Tuesday, May 25, 2021 5:03 PM > *To:* Jay K ; Mika Nystrom > *Cc:* m3devel > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > Modula-3's handling of full-range unsigned arithmetic has a fundamental > difference from C and most other languages.? I wrote a thing a few years > ago about the language design pros and cons here.? It's a case where you > have two of three desirable properties, and Modula-3 chose differently, > at least of the languages I am familiar with.? I'll see if I can find > the article. > > But language design issues aside, here is the way Modula-3 specifies, > and what an implementation must, well, implement. > > In C and many others, there are different types for signed and unsigned > full-range integers.? Then the arithmetic operators, '+' are overloaded. > An implementation must translate them to use different arithmetic, depending > on the type of the operands, similar to integer vs. floating arithmetic. > > In Modula-3 instead, there are different, non-overloaded operators for > the two kinds of arithmetic.? '+', etc. always use signed arithmetic. > 'Word.Plus' and cousins always use unsigned.? Since the type is not > used to distinguish which arithmetic, there are not two types. > INTEGER=Word.T.? They are just different names for the same type. > Either group of operators can be applied to it. > > Since C's semantics are different, preserving Modula-3's in C has > to look different.? You have to use the choice of which kind of > Modula-3 operator to case the operands to the appropriate signedness, > so C's one overloaded operator will get translated by the C compiler. > > As an aside, as a kind of documentation, I always declare a variable > INTEGER if its value is treated as signed and Word.T otherwise.? This > makes it a lot easier for me, at least, and hopefully any other readers, > to keep track of things.? But it's not linguistically different. > > Additionally, Modula-3 always does arithmetic of either kind in a > full-range field, including intermediate expression results, even > if the operand and/or result variables are are subranges.? Thus, > extension and truncation occur only on loads and stores.? This > hugely simplifies both language definition and code when dealing > with mixed sizes. I can personally attest from much hard > experience that writing overflow-tolerant code can be a nightmare > when intermediate results get promoted to the larger of the > operands or such rules. > > Note that this means that a subrange must fit either in a full-range > signed or full-range unsigned field, but can't span them both. > This a front-end static check.? Subrange bounds are always interpreted > as signed, has created some confusion in the past.? But the signed > 32-bit values and the unsigned 32-bit values all fit in a single > 64-bit word, treated as signed, so a subrange can be either, with > the bounds specified as signed constants. > > As for overflows, Word defines its operations to be modulo Word.t, > treated as unsigned.? Signed operators aren't defined WRT overflow. > > For calling handwritten C code from Modula-3, the C programmer will > need some way of knowing whether a full-range int should be > treated as signed or unsigned.? I can't think of a way to provide > static type checking of this, but that is nothing new across a language > boundary.? I guess the Modula-3 extern signature needs a comment and > the C prototype can declare the signedness explicitly. > > On 5/25/21 12:19 AM, Jay K wrote: >> Aside: >> >> Here is an example of the inconsistency btw: >> >> WebInfo.m3: import_procedure name:Word__Divide return_typename:INTEGER >> Word.i3: ? ?import_procedure name:Word__Divide return_typename:Word__T >> >> I freely admit, return_typename is new, I added it. >> So no preexisting code cares. >> In both cases it resolves to signed INT64. >>? ? So even new code is pretty ok. >> >> But I kinda do need to fix it and it is kinda wrong/bad. >> >> The reason is: >>? ? - I want to concatenate all the m3c output. >>? ? - Including with hand written C. >>? ? ? ?Maybe this is misguided. But it'll enable redistributing >>? ? ? ?a minimal cm3 system as one large file. >>? ? ? ?It makes some sense on a per-library basis as well, for >>? ? ? ?"old fashioned LTCG". But incremental build suffers, and >>? ? ? ?it stresses the C compiler. >> >> So every m3c file should be consistent with other m3c files. >> i.e. if they declare the same function, it should have the same signature. >> >> Ok, so typedef something INTEGER; typedef INTEGER Word__T; >> >> But also chosen interpretation of Word.T/Word__T should make sense for hand written C. >> C functions should be able to take Word.T..well..maybe. Maybe no need. >> >> It isn't a big huge deal but it is something. >> >> I had actually introduced WORD_T (all caps, single underscore) for functions >> taking Word.T, years ago. And for internal use, as being approximately >> size_t or uintptr_t, which it matches on most systems (except VMS..) >> >> Perhaps having one type for Modula3 and another for C is an ok idea. >> Signed for Modula3, unsigned for C. >> But it was done by accident, and the names reflect that. >> >> I'm still not sure what to do, but there isn't an immediate problem. >> I am still working through more m3c/C divergences. >> >>? ? > (BTW, other parts of the language spec ensure that the behavior >>? ? > resulting from not checking the range is not an "unchecked runtime error".) >> >> Please avoid double/triple negatives. >> I think you are saying, that even in the face of silent integer overflow, we will not suffer any thing else, like array overflow or double free. >> >>? ?> Yes < is not really meaningful on integers MOD 2^N. >> >> Well, what I meant was, Word.T < does a signed comparison. >> It is the intent that Word32.T{0x80000000} > Word32.T{0x7FFFFFFF}, but it is not. >> It only works if you call Word.LT. >> >> I understand your point, that in the face of silent rollover you also get the wrong result. >> This is the contract of Word.T and the ambivalent contract of INTEGER. >> >> Relevant aside: I think only Rust has really tried to solve this, with the obvious semi-complicated outcome: You need a bunch of types and let the user decide on a case by case basis. But hopefully good defaults. >> >>? ?> They are inconvenient to implement on most hardware... >> >> Not at all, "just" inefficient if portable, or a few machine specific nonportable options. >> Too much to write here and now. >> >>? ?> Yes, I think that's a good summary. >> >> Can/should we fix/change it? >> Like, should I be able to make an opaque type, that has a known constant size and is "blittable" (memcpy-able, maybe memcmp-able)? >> Are small records enough? >> >>? ?- Jay >> >> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >> *From:* Mika Nystrom >> *Sent:* Monday, May 24, 2021 11:18 PM >> *To:* Jay K >> *Cc:* m3devel >> *Subject:* Re: [M3devel] Word.T rules vs. enforcement? >> >> >> On Mon, May 24, 2021 at 3:27 PM Jay K >> wrote: >> >>???? I am not criticizing the functions and their meaning in the Word interface per se. >> >>???? My problem is that Word.T, the type, is not actually limited >>???? by the language/interface, to be used only with the Word interface functions. >> >>???? Its INTEGER-ness is transparent. >> >>???? That seems bad. >> >>???? + is as you say, "in between". >>???? If the runtime traps signed integer overflow, then +Word.T is wrong. >>???? If the runtime allows silent signed integer overflow, then +Word.T is ok. >> >> >> No it's really not OK even if the implementation doesn't **happen to** check. >> >> I am not sure if it is spelled out anywhere, but it's clear to me that the intent of the language spec is that >> >> LAST(INTEGER)?+ 1 = LAST(Word.T)?+ 1 >> >> is a meaningless statement.? The system is definitely **allowed to** treat it as a fatal error.? It would be nice if it did, consistently. >> >> (BTW, other parts of the language spec ensure that the behavior resulting from not checking the range is not an "unchecked runtime error".) >> >> If you want it to wrap, use Word.Plus(LAST(Word.T),1)--that has the defined value FIRST(Word.T). >> >>???? But for example < is wrong, sometimes. >> >> >> Yes < is not really meaningful on integers MOD 2^N.? Does Wednesday come before Sunday or does Sunday come before Wednesday?? Both are obviously true. >> >> Of course there is "an order" on the integers MOD 2^N.. in fact there are (2^N)! ways of ordering them if all you want is "an order".? There is an obvious one, but it's not usually that useful in the context of algorithms that ALSO care about wrapping the integers.... it's a bit of a trap actually, and I guess that's what you mean by "< is wrong, sometimes". >> >> >>???? I think a record containing an INTEGER, or array of bytes, would be better. >>???? Even better if the language prevented anything from reading/writing the fields, >>???? except for the implementation of the interface -- basically like C++ private:, >>???? or private + friend (if the functions are not member functions). >> >>???? Modula-3 seems to do plenty ok with the features of heap allocated garbage >>???? collected types, but much less good with value types and stack allocated and >>???? globals. I think that is a good approximate summary -- the features are all there, >>???? just not allowed on all types, esp. not on the "efficient" types. >> >> >> Yes, I think that's a good summary. >> >> >>???? (I also think importing FooRep is too easy, but I guess that can be limited to the library >>???? and not importers, because of lowercase interface and capital Interface in quake?) >> >> >> apparently cm3 doesn't enforce that (anymore)? >> >> >>???? >not a machine-oriented definition. >> >>???? Everything needs to be somewhat machine-oriented, in order to be machine-implemented. >> >> >> Sure, that's why we live without the range checks.? They are inconvenient to implement on most hardware... >> >> >>????? ?- Jay >> >>???? ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >>???? *From:* Mika Nystrom >> >>???? *Sent:* Monday, May 24, 2021 10:02 PM >>???? *To:* Jay K >> >>???? *Cc:* m3devel >> >>???? *Subject:* Re: [M3devel] Word.T rules vs. enforcement? >>???? I'm not sure what the problem is here, but hmm, no?+ is generally not correct on Word.T. >> >>???? Integer overflow on INTEGERs in Modula-3 is defined as being an error, which may or may not be caught by the implementation. >> >>???? Usually when you use Word.T, you don't want integer overflow to be an error but to wrap. >> >>???? Word.T is an interface that interprets bits of type Word.T (usually = INTEGER) in a specific way.? Nothing else. >> >>???? INTEGER is really meant to look like Peano's numbers.? It's a mathematics-oriented definition, not a machine-oriented definition.? That's the Algol Way. >> >>???? If you want the machine-oriented view (where Word.T is an integer MOD 2^WordSize and all the operations are defined on such objects), use the facilities defined in the Word interface to manipulate those (same) bits. >> >>????? ? ? ?Mika >> >>???? P.S. I saw your emails about semaphores.? I will get to it I promise---I need to try to remember what I did there before responding. >> >>???? On Mon, May 24, 2021 at 2:31 PM Jay K >> wrote: >> >>???????? Oops, actually + is correct, assuming integer overflow is well defined..which I believe we are unfortunately ambivalent about. :( >> >>???????? - Jay >>???????? ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >>???????? *From:* M3devel >> on behalf of Jay K >> >>???????? *Sent:* Monday, May 24, 2021 1:56:22 PM >>???????? *To:* m3devel >> >>???????? *Subject:* [M3devel] Word.T rules vs. enforcement? >>???????? This Word.T business seems bad to me. >> >>???????? In that, it has plenty of rules, and no enforcement. >> >>???????? I get that assignment is pass through, works. >>???????? And compare for equality/inequality are ok. >> >>???????? But you are never supposed to use e.g. "+" on Word.T. >>???????? But yet, "+" does work on Word.T. It compiles. It is usually >>???????? correct. But it could go wrong for overflow. >>???????? And then "<" compiles, and works sometimes, and is wrong. >> >>???????? Is there a better way? >> >>???????? I'd suggest REFANY or traced pointer to branded record, but these have GC implications. >> >>???????? And untraced pointer is unsafe. >> >>???????? So stuck? >> >>???????? How about a record containing an integer, passed around by value? >>???????? That is still abusable but with more effort. >>???????? It could even be an array of char to dissuade manual use, that the implementation could cast away. >>???????? Of course, hoping/assuming the ABI deals efficiently with small records. >> >>???????? And then I have to decide what type C should use for Word.T. >>???????? I think it should actually be unsigned. >> >>????????? ?- Jay >>???????? _______________________________________________ >>???????? M3devel mailing list >>???????? M3devel at elegosoft.com > >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C81a7cce0eaee47372b4108d91f9f083f%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637575590164056044%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=Cf1%2BSJP8lRGwpojkoJ10bhg79gEpbYMvgTznYzdBSQM%3D&reserved=0 > > >> >> >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C81a7cce0eaee47372b4108d91f9f083f%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637575590164056044%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=Cf1%2BSJP8lRGwpojkoJ10bhg79gEpbYMvgTznYzdBSQM%3D&reserved=0 >> > > -- > Rodney Bates > rodney.m.bates at acm.org > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Thu May 27 00:10:26 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 26 May 2021 22:10:26 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> References: , <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> Message-ID: > PROCEDURE BadLT(a,b:Word.T):Word.T= > BEGIN > RETURN a < b; > END BadLT; These are "correct" because I merely misspelled INTEGER as Word.T? I get that the present Modula-3 language/compiler render no judgement, so they are correct, but wouldn't they be considered in very very bad form, so bad that, we really wish the compiler to reject them? Or that the language allowed us to state them a little differently, to help the compiler reject them? > Choosing which to declare something as is only equivalent > to a comment, though I would consider it a good one. This seems way too weak to me. "Word.T" ideally is a statement to the compiler, that, without LOOPHOLE or something to change its type, shall only have unsigned semantics. Some things we document with comments, some things we state with types. We should endeavor to state as much as possible with types, so that the compiler can check them. We should endeavor to have a language/type-system that minimizes what is only stateable in comments. Otherwise we might as well just have memory be an array of bytes that we loophole at every line. Eh? - Jay ________________________________ From: Rodney M. Bates Sent: Wednesday, May 26, 2021 9:40 PM To: Jay K ; Mika Nystrom ; rodney.m.bates at acm.org Cc: m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? No, you don't understand. You are trying to apply C semantics to Modula-3 On 5/26/21 2:33 AM, Jay K wrote: > I think I understand this pretty well. > > My initial complaint, is I want this incorrect code to fail to compile: > > PROCEDURE BadAdd(a,b:Word.T):Word.T= > BEGIN > RETURN a + b; > END BadAdd; > > PROCEDURE BadLT(a,b:Word.T):Word.T= > BEGIN > RETURN a < b; > END BadLT; These examples are correct, though not, IMO, the best style. They would be equivalent if Word.T were replaced by INTEGER. They would be also correct but different if '+' were replaced by Word.Plus. The determination of whether arithmetic is signed or unsigned comes from the operator: '+' or Word.Plus, not the type. It couldn't come from the type, because Word.T and INTEGER are just different names for the same type. Choosing which to declare something as is only equivalent to a comment, though I would consider it a good one. > > The fact that Word.T is INTEGER should not be visible to code outside of interface Word. > They should be forced to call into Word functions. > > Very much like C++ private or Modula-3 opaque types (which unfortunately must be pointers). > > The stated rules are ok. The problem is the compiler cannot enforce them. > > It might help to say: > > TYPE Word.T = RECORD opaque:INTEGER END; > > I realize it is an arms race. > I could still write: > > PROCEDURE BadAdd(a,b:Word.T):Word.T= > BEGIN > RETURN Word.T{a.opaque + b.opaque}; > END BadAdd; > > but at least not by accident (unless via copy/paste..) > > I also am not sure what to do.. > > Given an external C function declared as taking Word.T > what should the C function think its parameters are? > Signed or unsigned? If the C function is a translation of Module-3 code, it can declare it as either, but wherever the C code contains a translation of a Modula-3 operator, it must cast the operands to the signedness implied by the Modula-3 operator, if the C operand doesn't already have that signedness. If the C is handwritten, the writers of the two will have to agree on signedness outside the semantic rules of the two languages, which are different. But a nice practical convention would be if all Modula-3 programmers used INTEGER when signed interpretation of the operands is correct and Word.T when unsigned. Then a Modula-3 extern declaration for handwritten C could be mechanically translated to a C prototype, using this convention to determine the declared signedness of the C formal. Calling handwritten C from m3-to-C translated could follow the same convention. > > Worse given an external C function returning Word.T, what to do? > (M3front does/should issue range checks for return values of external functions? It maybe should.) If the type is INTEGER (or Word.T, same thing), then every bit pattern is a legal member of the type, so no range check could ever fail. If the result type is a subrange, then a check would be good. But remember, there are no full-range unsigned subranges. > > I admit I got confused. I was confusing Word.T with CARDINAL. > CARDINAL is half range, 0..LAST(INTEGER). Word.T is full range. > > That is, C code more often takes Word.T than CARDINAL. > So the answer is pretty clearly it should be full range unsigned. > > C code taking CARDINAL begs similar but more difficult question. > Maybe there is no such code and there shall be none. > > And then I was reminded, I also want a bunch more integer types/functions. > > For legacy reasons, signed and unsigned with silent overflow are needed. > But then there should be some sort of "trapping" types. > Where "trapping" could be defined in a few ways: > BOOLEAN return: TRUE for trap > BOOLEAN return: TRUE for "success", FALSE for "failure" > raise exception > an out BOOLEAN parameter named overflow > an out INTEGER parmeter named carry (i.e. building block for multiple precision math) > > For multiplication, an out INTEGER parameter named highPart or something. > For division, I am not sure, I hadn't considered it, but some "checked integer" libraries handle it. > > I realize at some point you want rational numbers, multiple precision, and automatic dynamic > conversion to a representation that best represents the value, including possible "an expression" > with deferred evaluation. > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Rodney M. Bates > *Sent:* Tuesday, May 25, 2021 5:03 PM > *To:* Jay K ; Mika Nystrom > *Cc:* m3devel > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > Modula-3's handling of full-range unsigned arithmetic has a fundamental > difference from C and most other languages. I wrote a thing a few years > ago about the language design pros and cons here. It's a case where you > have two of three desirable properties, and Modula-3 chose differently, > at least of the languages I am familiar with. I'll see if I can find > the article. > > But language design issues aside, here is the way Modula-3 specifies, > and what an implementation must, well, implement. > > In C and many others, there are different types for signed and unsigned > full-range integers. Then the arithmetic operators, '+' are overloaded. > An implementation must translate them to use different arithmetic, depending > on the type of the operands, similar to integer vs. floating arithmetic. > > In Modula-3 instead, there are different, non-overloaded operators for > the two kinds of arithmetic. '+', etc. always use signed arithmetic. > 'Word.Plus' and cousins always use unsigned. Since the type is not > used to distinguish which arithmetic, there are not two types. > INTEGER=Word.T. They are just different names for the same type. > Either group of operators can be applied to it. > > Since C's semantics are different, preserving Modula-3's in C has > to look different. You have to use the choice of which kind of > Modula-3 operator to case the operands to the appropriate signedness, > so C's one overloaded operator will get translated by the C compiler. > > As an aside, as a kind of documentation, I always declare a variable > INTEGER if its value is treated as signed and Word.T otherwise. This > makes it a lot easier for me, at least, and hopefully any other readers, > to keep track of things. But it's not linguistically different. > > Additionally, Modula-3 always does arithmetic of either kind in a > full-range field, including intermediate expression results, even > if the operand and/or result variables are are subranges. Thus, > extension and truncation occur only on loads and stores. This > hugely simplifies both language definition and code when dealing > with mixed sizes. I can personally attest from much hard > experience that writing overflow-tolerant code can be a nightmare > when intermediate results get promoted to the larger of the > operands or such rules. > > Note that this means that a subrange must fit either in a full-range > signed or full-range unsigned field, but can't span them both. > This a front-end static check. Subrange bounds are always interpreted > as signed, has created some confusion in the past. But the signed > 32-bit values and the unsigned 32-bit values all fit in a single > 64-bit word, treated as signed, so a subrange can be either, with > the bounds specified as signed constants. > > As for overflows, Word defines its operations to be modulo Word.t, > treated as unsigned. Signed operators aren't defined WRT overflow. > > For calling handwritten C code from Modula-3, the C programmer will > need some way of knowing whether a full-range int should be > treated as signed or unsigned. I can't think of a way to provide > static type checking of this, but that is nothing new across a language > boundary. I guess the Modula-3 extern signature needs a comment and > the C prototype can declare the signedness explicitly. > > On 5/25/21 12:19 AM, Jay K wrote: >> Aside: >> >> Here is an example of the inconsistency btw: >> >> WebInfo.m3: import_procedure name:Word__Divide return_typename:INTEGER >> Word.i3: import_procedure name:Word__Divide return_typename:Word__T >> >> I freely admit, return_typename is new, I added it. >> So no preexisting code cares. >> In both cases it resolves to signed INT64. >> So even new code is pretty ok. >> >> But I kinda do need to fix it and it is kinda wrong/bad. >> >> The reason is: >> - I want to concatenate all the m3c output. >> - Including with hand written C. >> Maybe this is misguided. But it'll enable redistributing >> a minimal cm3 system as one large file. >> It makes some sense on a per-library basis as well, for >> "old fashioned LTCG". But incremental build suffers, and >> it stresses the C compiler. >> >> So every m3c file should be consistent with other m3c files. >> i.e. if they declare the same function, it should have the same signature. >> >> Ok, so typedef something INTEGER; typedef INTEGER Word__T; >> >> But also chosen interpretation of Word.T/Word__T should make sense for hand written C. >> C functions should be able to take Word.T..well..maybe. Maybe no need. >> >> It isn't a big huge deal but it is something. >> >> I had actually introduced WORD_T (all caps, single underscore) for functions >> taking Word.T, years ago. And for internal use, as being approximately >> size_t or uintptr_t, which it matches on most systems (except VMS..) >> >> Perhaps having one type for Modula3 and another for C is an ok idea. >> Signed for Modula3, unsigned for C. >> But it was done by accident, and the names reflect that. >> >> I'm still not sure what to do, but there isn't an immediate problem. >> I am still working through more m3c/C divergences. >> >> > (BTW, other parts of the language spec ensure that the behavior >> > resulting from not checking the range is not an "unchecked runtime error".) >> >> Please avoid double/triple negatives. >> I think you are saying, that even in the face of silent integer overflow, we will not suffer any thing else, like array overflow or double free. >> >> > Yes < is not really meaningful on integers MOD 2^N. >> >> Well, what I meant was, Word.T < does a signed comparison. >> It is the intent that Word32.T{0x80000000} > Word32.T{0x7FFFFFFF}, but it is not. >> It only works if you call Word.LT. >> >> I understand your point, that in the face of silent rollover you also get the wrong result. >> This is the contract of Word.T and the ambivalent contract of INTEGER. >> >> Relevant aside: I think only Rust has really tried to solve this, with the obvious semi-complicated outcome: You need a bunch of types and let the user decide on a case by case basis. But hopefully good defaults. >> >> > They are inconvenient to implement on most hardware... >> >> Not at all, "just" inefficient if portable, or a few machine specific nonportable options. >> Too much to write here and now. >> >> > Yes, I think that's a good summary. >> >> Can/should we fix/change it? >> Like, should I be able to make an opaque type, that has a known constant size and is "blittable" (memcpy-able, maybe memcmp-able)? >> Are small records enough? >> >> - Jay >> >> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >> *From:* Mika Nystrom >> *Sent:* Monday, May 24, 2021 11:18 PM >> *To:* Jay K >> *Cc:* m3devel >> *Subject:* Re: [M3devel] Word.T rules vs. enforcement? >> >> >> On Mon, May 24, 2021 at 3:27 PM Jay K >> wrote: >> >> I am not criticizing the functions and their meaning in the Word interface per se. >> >> My problem is that Word.T, the type, is not actually limited >> by the language/interface, to be used only with the Word interface functions. >> >> Its INTEGER-ness is transparent. >> >> That seems bad. >> >> + is as you say, "in between". >> If the runtime traps signed integer overflow, then +Word.T is wrong. >> If the runtime allows silent signed integer overflow, then +Word.T is ok. >> >> >> No it's really not OK even if the implementation doesn't **happen to** check. >> >> I am not sure if it is spelled out anywhere, but it's clear to me that the intent of the language spec is that >> >> LAST(INTEGER) + 1 = LAST(Word.T) + 1 >> >> is a meaningless statement. The system is definitely **allowed to** treat it as a fatal error. It would be nice if it did, consistently. >> >> (BTW, other parts of the language spec ensure that the behavior resulting from not checking the range is not an "unchecked runtime error".) >> >> If you want it to wrap, use Word.Plus(LAST(Word.T),1)--that has the defined value FIRST(Word.T). >> >> But for example < is wrong, sometimes. >> >> >> Yes < is not really meaningful on integers MOD 2^N. Does Wednesday come before Sunday or does Sunday come before Wednesday? Both are obviously true. >> >> Of course there is "an order" on the integers MOD 2^N.. in fact there are (2^N)! ways of ordering them if all you want is "an order". There is an obvious one, but it's not usually that useful in the context of algorithms that ALSO care about wrapping the integers.... it's a bit of a trap actually, and I guess that's what you mean by "< is wrong, sometimes". >> >> >> I think a record containing an INTEGER, or array of bytes, would be better. >> Even better if the language prevented anything from reading/writing the fields, >> except for the implementation of the interface -- basically like C++ private:, >> or private + friend (if the functions are not member functions). >> >> Modula-3 seems to do plenty ok with the features of heap allocated garbage >> collected types, but much less good with value types and stack allocated and >> globals. I think that is a good approximate summary -- the features are all there, >> just not allowed on all types, esp. not on the "efficient" types. >> >> >> Yes, I think that's a good summary. >> >> >> (I also think importing FooRep is too easy, but I guess that can be limited to the library >> and not importers, because of lowercase interface and capital Interface in quake?) >> >> >> apparently cm3 doesn't enforce that (anymore)? >> >> >> >not a machine-oriented definition. >> >> Everything needs to be somewhat machine-oriented, in order to be machine-implemented. >> >> >> Sure, that's why we live without the range checks. They are inconvenient to implement on most hardware... >> >> >> - Jay >> >> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >> *From:* Mika Nystrom >> >> *Sent:* Monday, May 24, 2021 10:02 PM >> *To:* Jay K >> >> *Cc:* m3devel >> >> *Subject:* Re: [M3devel] Word.T rules vs. enforcement? >> I'm not sure what the problem is here, but hmm, no + is generally not correct on Word.T. >> >> Integer overflow on INTEGERs in Modula-3 is defined as being an error, which may or may not be caught by the implementation. >> >> Usually when you use Word.T, you don't want integer overflow to be an error but to wrap. >> >> Word.T is an interface that interprets bits of type Word.T (usually = INTEGER) in a specific way. Nothing else. >> >> INTEGER is really meant to look like Peano's numbers. It's a mathematics-oriented definition, not a machine-oriented definition. That's the Algol Way. >> >> If you want the machine-oriented view (where Word.T is an integer MOD 2^WordSize and all the operations are defined on such objects), use the facilities defined in the Word interface to manipulate those (same) bits. >> >> Mika >> >> P.S. I saw your emails about semaphores. I will get to it I promise---I need to try to remember what I did there before responding. >> >> On Mon, May 24, 2021 at 2:31 PM Jay K >> wrote: >> >> Oops, actually + is correct, assuming integer overflow is well defined..which I believe we are unfortunately ambivalent about. :( >> >> - Jay >> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >> *From:* M3devel >> on behalf of Jay K >> >> *Sent:* Monday, May 24, 2021 1:56:22 PM >> *To:* m3devel >> >> *Subject:* [M3devel] Word.T rules vs. enforcement? >> This Word.T business seems bad to me. >> >> In that, it has plenty of rules, and no enforcement. >> >> I get that assignment is pass through, works. >> And compare for equality/inequality are ok. >> >> But you are never supposed to use e.g. "+" on Word.T. >> But yet, "+" does work on Word.T. It compiles. It is usually >> correct. But it could go wrong for overflow. >> And then "<" compiles, and works sometimes, and is wrong. >> >> Is there a better way? >> >> I'd suggest REFANY or traced pointer to branded record, but these have GC implications. >> >> And untraced pointer is unsafe. >> >> So stuck? >> >> How about a record containing an integer, passed around by value? >> That is still abusable but with more effort. >> It could even be an array of char to dissuade manual use, that the implementation could cast away. >> Of course, hoping/assuming the ABI deals efficiently with small records. >> >> And then I have to decide what type C should use for Word.T. >> I think it should actually be unsigned. >> >> - Jay >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com > >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C495c6c4aa6f142732f4a08d9208ee758%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637576620410098240%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=Ql8B%2Bxt3dJsY3fpYMUj6N2cC6PrctrRkmJVYg4W%2BRys%3D&reserved=0 > > >> >> >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C495c6c4aa6f142732f4a08d9208ee758%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637576620410108238%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=36aiB2pFTmru%2B66w7AoiPfU5225sG8TZJ5f9idHJud8%3D&reserved=0 >> > > -- > Rodney Bates > rodney.m.bates at acm.org > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C495c6c4aa6f142732f4a08d9208ee758%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637576620410108238%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=36aiB2pFTmru%2B66w7AoiPfU5225sG8TZJ5f9idHJud8%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 27 00:21:57 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 26 May 2021 22:21:57 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> , Message-ID: Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). - Jay ________________________________ From: Mika Nystrom Sent: Wednesday, May 26, 2021 3:17 PM To: Jay K Cc: rodney.m.bates at acm.org; m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? Sorry I can't resist, and at risk of being repetitive: Some things we document with comments, some things we state with types. We should endeavor to state as much as possible with types, so that the compiler can check them. We should endeavor to have a language/type-system that minimizes what is only stateable in comments. The language is called Modula. Express your intent with modules. -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 27 00:27:48 2021 From: jayk123 at hotmail.com (Jay K) Date: Wed, 26 May 2021 22:27:48 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> , , Message-ID: > One way of thinking of it is, > Word.T maybe originally was > an OBJECT or opaque type > using a REFANY. > Then as an optimization we > made Word.T = INTEGER Right. The optimization removed the types. Ideally we have both, types and optimization. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 26, 2021 3:21:57 PM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). - Jay ________________________________ From: Mika Nystrom Sent: Wednesday, May 26, 2021 3:17 PM To: Jay K Cc: rodney.m.bates at acm.org; m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? Sorry I can't resist, and at risk of being repetitive: Some things we document with comments, some things we state with types. We should endeavor to state as much as possible with types, so that the compiler can check them. We should endeavor to have a language/type-system that minimizes what is only stateable in comments. The language is called Modula. Express your intent with modules. -------------- next part -------------- An HTML attachment was scrubbed... URL: From hendrik at topoi.pooq.com Thu May 27 03:06:00 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Wed, 26 May 2021 21:06:00 -0400 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> Message-ID: <20210527010600.am7fuyidv3ikqtiz@topoi.pooq.com> On Wed, May 26, 2021 at 10:21:57PM +0000, Jay K wrote: > Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. > > Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). The whole point of the Word module is that its operations alow you to get at the machine representation of integers and handle them just the way I want. Allowing signed and unsigned operations on them is just what I want when I use Word.T. For most purposes, if I want only nonnegative integers, I use CARDINAL. -- hendrik From jayk123 at hotmail.com Thu May 27 03:07:40 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 27 May 2021 01:07:40 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> , , , Message-ID: If a value can be represented by some variable of type T, then we say that the value is a member of T and T contains the value. > It so happens that the type Word.T > has the same set of values as the > type INTEGER. Do they really? I thought Word.T is meant to represent full range unsigned numbers. Not just guaranteed non-trapping overflow. Right? Thus Word.LT, not just Word.Add. Word.Add turns out to just be non trapping, because the results are the same for signed and unsigned. But Word.T provides a different interpretation and therefore ?value?. They have the same set of values as viewed in untyped memory. Very similar to pointers and floats. And therein is my point also. Sort of. We don?t use Pointer.Deference, Float.Add, Set.Intersect. Those types are stronger (those modules essentially nonexistent) allowing the compiler to understand the code much better, and possibly correctly overload operators, contrary to Word.T. We could just make everything an ?integer? and require ?just? using the correct functions eh? Even if you say set of pointer values and maybe float values are fewer, both not obvious, set remains. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 26, 2021 3:27:48 PM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? > One way of thinking of it is, > Word.T maybe originally was > an OBJECT or opaque type > using a REFANY. > Then as an optimization we > made Word.T = INTEGER Right. The optimization removed the types. Ideally we have both, types and optimization. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 26, 2021 3:21:57 PM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). - Jay ________________________________ From: Mika Nystrom Sent: Wednesday, May 26, 2021 3:17 PM To: Jay K Cc: rodney.m.bates at acm.org; m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? Sorry I can't resist, and at risk of being repetitive: Some things we document with comments, some things we state with types. We should endeavor to state as much as possible with types, so that the compiler can check them. We should endeavor to have a language/type-system that minimizes what is only stateable in comments. The language is called Modula. Express your intent with modules. -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 27 03:14:50 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 27 May 2021 01:14:50 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: <20210527010600.am7fuyidv3ikqtiz@topoi.pooq.com> References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> , <20210527010600.am7fuyidv3ikqtiz@topoi.pooq.com> Message-ID: Aha I looked. Word combines multiple things. Yuck. I thought it was just one thing: full range unsigned non-trapping integers. ?A Word.T w represents a sequence of Word.Size bits w_0, ..., w_(Word.Size-1). It also represents the unsigned number SUM (w_i * 2^i). Finally, it also represents a signed INTEGER by some implementation-dependent encoding (for example, two's complement). The built-in operations of the language deal with the signed value; the operations in this interface deal with the unsigned value or with the bit sequence.? - Jay ________________________________ From: M3devel on behalf of Hendrik Boom Sent: Wednesday, May 26, 2021 6:06:00 PM To: m3devel at elegosoft.com Subject: Re: [M3devel] Word.T rules vs. enforcement? On Wed, May 26, 2021 at 10:21:57PM +0000, Jay K wrote: > Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. > > Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). The whole point of the Word module is that its operations alow you to get at the machine representation of integers and handle them just the way I want. Allowing signed and unsigned operations on them is just what I want when I use Word.T. For most purposes, if I want only nonnegative integers, I use CARDINAL. -- hendrik _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C01a577354f074317e45308d920aba4cc%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637576743841192267%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=b%2FlADqJMXCD4OTtPW8mbDj7rtRLQ%2F0hXSMGLi%2Bj6tGw%3D&reserved=0 -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 27 08:59:10 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 27 May 2021 06:59:10 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> , , , , Message-ID: There more seeming problems here. Ctypes.unsigned_int, unsigned_long, unsigned_long_long are also sometimes signed, depending on 32bit vs. 64bit. This feels completely backwards. Their name says "unsigned", yet they are not. They are INTEGER / LONGINT. They are Word.T / Long.T. (again, depending on which type and target; some are just subranges and I think end up unsigned). They "work", they "do something", I can define it pretty clearly (except for the weasel wording about overflow) but yet their name and their behavior are opposite. It does not make complete sense to me, to say their meanings are determined by the module they occur in, or even their value set alone. The module-based definition only makes sense to me, mostly, if there is some opacity. If every type was just an array of bytes and the module gave the size. (and if even compare and assign was delegated to the module). So I would have to pass the bytes to the module that defines the type. The meaning of a type includes the operations that act on them, and what those operations do. This is why "OO syntax" resonates so well: object.function, dispatches statically or dynamically, to the functions that are part of the type of object; the type of a variable and the functions you can call on it become fairly tightly bound; not perfectly, but fairly well. I think I will have to special case these in m3c (the Ctypes.unsigned) and ignore that m3front tells me they are INTEGER/LONGINT. :( > And yeah this is not a very satisfying answer if what you really care about is interoperability with C. C interop is not my point or my entire point. I am finding this stuff due to, related to, C interop. But I've said it already. Writing: VAR a,b:Word.T; EVAL a < b; should not be legal. "Word" is "unsigned" and "<" is signed. The type/module and operation have a varying interpretation of the bytes. It is like there is a cast/loophole in there. At least on the prior basis of Word.T being strictly about full range unsigned integers. That the comments indicate it has three purposes makes it wierd to me. - Jay ________________________________ From: Jay K Sent: Thursday, May 27, 2021 1:07 AM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? If a value can be represented by some variable of type T, then we say that the value is a member of T and T contains the value. > It so happens that the type Word.T > has the same set of values as the > type INTEGER. Do they really? I thought Word.T is meant to represent full range unsigned numbers. Not just guaranteed non-trapping overflow. Right? Thus Word.LT, not just Word.Add. Word.Add turns out to just be non trapping, because the results are the same for signed and unsigned. But Word.LT provides a different interpretation and therefore ?value?. They have the same set of values as viewed in untyped memory. Very similar to pointers and floats. And therein is my point also. Sort of. We don?t use Pointer.Deference, Float.Add, Set.Intersect. Those types are stronger (those modules essentially nonexistent) allowing the compiler to understand the code much better, and possibly correctly overload operators, contrary to Word.T. We could just make everything an ?integer? and require ?just? using the correct functions eh? Even if you say set of pointer values and maybe float values are fewer, both not obvious, set remains. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 26, 2021 3:27:48 PM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? > One way of thinking of it is, > Word.T maybe originally was > an OBJECT or opaque type > using a REFANY. > Then as an optimization we > made Word.T = INTEGER Right. The optimization removed the types. Ideally we have both, types and optimization. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 26, 2021 3:21:57 PM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). - Jay ________________________________ From: Mika Nystrom Sent: Wednesday, May 26, 2021 3:17 PM To: Jay K Cc: rodney.m.bates at acm.org; m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? Sorry I can't resist, and at risk of being repetitive: Some things we document with comments, some things we state with types. We should endeavor to state as much as possible with types, so that the compiler can check them. We should endeavor to have a language/type-system that minimizes what is only stateable in comments. The language is called Modula. Express your intent with modules. -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Thu May 27 09:42:14 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 27 May 2021 07:42:14 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> , , , , , Message-ID: To belabor the point, we presently get both: /* declare_typename typeid:T195C2A74 name:Cstddef__size_t */ /* declare_typename typeid:T195C2A74 name:Cstddef__ssize_t */ These cannot both be correct, right? I'll try to cope with it. 195C2A74 is INTEGER. I have a feeling, we could use full range unsigned integers in the language. I grant that this case might only be related to C interop. There's no other need for Cstddef, Cstdint, Ctypes, etc. And, if it is only for C interop, maybe full range unsigned integers are not all that valuable. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Thursday, May 27, 2021 6:59 AM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? There more seeming problems here. Ctypes.unsigned_int, unsigned_long, unsigned_long_long are also sometimes signed, depending on 32bit vs. 64bit. This feels completely backwards. Their name says "unsigned", yet they are not. They are INTEGER / LONGINT. They are Word.T / Long.T. (again, depending on which type and target; some are just subranges and I think end up unsigned). They "work", they "do something", I can define it pretty clearly (except for the weasel wording about overflow) but yet their name and their behavior are opposite. It does not make complete sense to me, to say their meanings are determined by the module they occur in, or even their value set alone. The module-based definition only makes sense to me, mostly, if there is some opacity. If every type was just an array of bytes and the module gave the size. (and if even compare and assign was delegated to the module). So I would have to pass the bytes to the module that defines the type. The meaning of a type includes the operations that act on them, and what those operations do. This is why "OO syntax" resonates so well: object.function, dispatches statically or dynamically, to the functions that are part of the type of object; the type of a variable and the functions you can call on it become fairly tightly bound; not perfectly, but fairly well. I think I will have to special case these in m3c (the Ctypes.unsigned) and ignore that m3front tells me they are INTEGER/LONGINT. :( > And yeah this is not a very satisfying answer if what you really care about is interoperability with C. C interop is not my point or my entire point. I am finding this stuff due to, related to, C interop. But I've said it already. Writing: VAR a,b:Word.T; EVAL a < b; should not be legal. "Word" is "unsigned" and "<" is signed. The type/module and operation have a varying interpretation of the bytes. It is like there is a cast/loophole in there. At least on the prior basis of Word.T being strictly about full range unsigned integers. That the comments indicate it has three purposes makes it wierd to me. - Jay ________________________________ From: Jay K Sent: Thursday, May 27, 2021 1:07 AM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? If a value can be represented by some variable of type T, then we say that the value is a member of T and T contains the value. > It so happens that the type Word.T > has the same set of values as the > type INTEGER. Do they really? I thought Word.T is meant to represent full range unsigned numbers. Not just guaranteed non-trapping overflow. Right? Thus Word.LT, not just Word.Add. Word.Add turns out to just be non trapping, because the results are the same for signed and unsigned. But Word.LT provides a different interpretation and therefore ?value?. They have the same set of values as viewed in untyped memory. Very similar to pointers and floats. And therein is my point also. Sort of. We don?t use Pointer.Deference, Float.Add, Set.Intersect. Those types are stronger (those modules essentially nonexistent) allowing the compiler to understand the code much better, and possibly correctly overload operators, contrary to Word.T. We could just make everything an ?integer? and require ?just? using the correct functions eh? Even if you say set of pointer values and maybe float values are fewer, both not obvious, set remains. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 26, 2021 3:27:48 PM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? > One way of thinking of it is, > Word.T maybe originally was > an OBJECT or opaque type > using a REFANY. > Then as an optimization we > made Word.T = INTEGER Right. The optimization removed the types. Ideally we have both, types and optimization. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 26, 2021 3:21:57 PM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). - Jay ________________________________ From: Mika Nystrom Sent: Wednesday, May 26, 2021 3:17 PM To: Jay K Cc: rodney.m.bates at acm.org; m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? Sorry I can't resist, and at risk of being repetitive: Some things we document with comments, some things we state with types. We should endeavor to state as much as possible with types, so that the compiler can check them. We should endeavor to have a language/type-system that minimizes what is only stateable in comments. The language is called Modula. Express your intent with modules. -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Fri May 28 00:45:30 2021 From: jayk123 at hotmail.com (Jay K) Date: Thu, 27 May 2021 22:45:30 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> , , , , , , Message-ID: again.. > declare_typename INT64 Cstddef__ssize_t > declare_typename INT64 Cstddef__size_t This seems so very clearly wrong to me. I kinda think, the language really needs types INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64. Or at least some of those. The 8 and 16 bit subranges in Ctypes are likely adequate. INT32 is already ok. UINT32 on 64bit targets is already ok. INT64 is already ok. But UINT64 on 64bit, and UINT32 on 32bit, definitely seem like problems. Their names suggest unsigned but they are actually signed, and they are the same types as other signed type. The underlying m3cg system has all of these distinct types. It seems pretty well defined. It is also kinda, like, if somehow integer was 33 bits or 65 bits then we could just use subranges? Maybe that would work internally? TYPE UINT64 = BITS 64 FOR [0..2^64-1] of some sort of internal integer type? and then use that for Word.T? TYPE INTEGER = BITS 64 FOR [-1..2^64-1] of some sort of internal integer type? Is something like that feasible? I'm not sure what to do. Maybe the answer is, like, unsigned types are not all that worthwhile. If 15 bits isn't enough magnitude, then nor is likely 16. So use INT32 in place of UINT16. And so on down the line, until you get to INT64 which should be large enough, you don't really need UINT64. Maybe if I just change all of m3core to use signed integers in the C signatures.. and review the code, which is mostly pass through anyway, so there is no interpretation as signed or unsigned, and slight mismatches upon pass through are ok. Maybe that will suffice. Or I could special case some names in m3c, and ignore the unsigned typenames. Or m3front could strive to have a typename for every parameter. For example, a "problem" is indirect/var/readonly types. <*externall*> PROCEDURE f(VAR a:TFoo); cannot have typename TFoo. Nor do type hashes work for my purposes (I need to be able to declare it in reasonable C). Maybe a convention where m3front synthesizes TFoo_star, following the Ctypes pattern? That would also help/fix the problem. 'cause we have <*external*> PROCEDURE TimeStuff(VAR a:TimeType); making that TypeTypePtr = REF TimeType; PROCEDURE TimeStuff(a:TimeTypePtr) would also work, but that turns safe into unsafe. I think first I'll try to remove the actual uses of the unsigned types in signatures. At least 32bit and 64bit. 8 and 16 are ok. - Jay ________________________________ From: Jay K Sent: Thursday, May 27, 2021 7:42 AM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? To belabor the point, we presently get both: /* declare_typename typeid:T195C2A74 name:Cstddef__size_t */ /* declare_typename typeid:T195C2A74 name:Cstddef__ssize_t */ These cannot both be correct, right? I'll try to cope with it. 195C2A74 is INTEGER. I have a feeling, we could use full range unsigned integers in the language. I grant that this case might only be related to C interop. There's no other need for Cstddef, Cstdint, Ctypes, etc. And, if it is only for C interop, maybe full range unsigned integers are not all that valuable. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Thursday, May 27, 2021 6:59 AM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? There more seeming problems here. Ctypes.unsigned_int, unsigned_long, unsigned_long_long are also sometimes signed, depending on 32bit vs. 64bit. This feels completely backwards. Their name says "unsigned", yet they are not. They are INTEGER / LONGINT. They are Word.T / Long.T. (again, depending on which type and target; some are just subranges and I think end up unsigned). They "work", they "do something", I can define it pretty clearly (except for the weasel wording about overflow) but yet their name and their behavior are opposite. It does not make complete sense to me, to say their meanings are determined by the module they occur in, or even their value set alone. The module-based definition only makes sense to me, mostly, if there is some opacity. If every type was just an array of bytes and the module gave the size. (and if even compare and assign was delegated to the module). So I would have to pass the bytes to the module that defines the type. The meaning of a type includes the operations that act on them, and what those operations do. This is why "OO syntax" resonates so well: object.function, dispatches statically or dynamically, to the functions that are part of the type of object; the type of a variable and the functions you can call on it become fairly tightly bound; not perfectly, but fairly well. I think I will have to special case these in m3c (the Ctypes.unsigned) and ignore that m3front tells me they are INTEGER/LONGINT. :( > And yeah this is not a very satisfying answer if what you really care about is interoperability with C. C interop is not my point or my entire point. I am finding this stuff due to, related to, C interop. But I've said it already. Writing: VAR a,b:Word.T; EVAL a < b; should not be legal. "Word" is "unsigned" and "<" is signed. The type/module and operation have a varying interpretation of the bytes. It is like there is a cast/loophole in there. At least on the prior basis of Word.T being strictly about full range unsigned integers. That the comments indicate it has three purposes makes it wierd to me. - Jay ________________________________ From: Jay K Sent: Thursday, May 27, 2021 1:07 AM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? If a value can be represented by some variable of type T, then we say that the value is a member of T and T contains the value. > It so happens that the type Word.T > has the same set of values as the > type INTEGER. Do they really? I thought Word.T is meant to represent full range unsigned numbers. Not just guaranteed non-trapping overflow. Right? Thus Word.LT, not just Word.Add. Word.Add turns out to just be non trapping, because the results are the same for signed and unsigned. But Word.LT provides a different interpretation and therefore ?value?. They have the same set of values as viewed in untyped memory. Very similar to pointers and floats. And therein is my point also. Sort of. We don?t use Pointer.Deference, Float.Add, Set.Intersect. Those types are stronger (those modules essentially nonexistent) allowing the compiler to understand the code much better, and possibly correctly overload operators, contrary to Word.T. We could just make everything an ?integer? and require ?just? using the correct functions eh? Even if you say set of pointer values and maybe float values are fewer, both not obvious, set remains. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 26, 2021 3:27:48 PM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? > One way of thinking of it is, > Word.T maybe originally was > an OBJECT or opaque type > using a REFANY. > Then as an optimization we > made Word.T = INTEGER Right. The optimization removed the types. Ideally we have both, types and optimization. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Wednesday, May 26, 2021 3:21:57 PM To: Mika Nystrom Cc: m3devel ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). - Jay ________________________________ From: Mika Nystrom Sent: Wednesday, May 26, 2021 3:17 PM To: Jay K Cc: rodney.m.bates at acm.org; m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? Sorry I can't resist, and at risk of being repetitive: Some things we document with comments, some things we state with types. We should endeavor to state as much as possible with types, so that the compiler can check them. We should endeavor to have a language/type-system that minimizes what is only stateable in comments. The language is called Modula. Express your intent with modules. -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Fri May 28 03:30:52 2021 From: jayk123 at hotmail.com (Jay K) Date: Fri, 28 May 2021 01:30:52 +0000 Subject: [M3devel] expand typeids to 64bits? Message-ID: Should we expand typeids to 64bits? To allow for a better hash or encoding function? i.e. fewer collisions, or encode some data directly, like size? I assume memory usage is ok. If 32bits weren't too much 25 years ago, 64 bits are probably ok today. Memories have far more than doubled.. Do we need pickle compatibilty? That'd seem a big reason why not. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Fri May 28 23:12:16 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Fri, 28 May 2021 16:12:16 -0500 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> <20210527010600.am7fuyidv3ikqtiz@topoi.pooq.com> Message-ID: On 5/26/21 8:14 PM, Jay K wrote: > Aha I looked. > Word combines multiple things. Yuck. > I thought it was just one thing: full range unsigned non-trapping integers. > > ?AWord.Twrepresents a sequence ofWord.Sizebitsw_0, ...,w_(Word.Size-1). It also represents the unsigned numberSUM (w_i * 2^i). Finally, it also represents a signedINTEGERby some implementation-dependent encoding (for example, two's complement). The built-in operations of the language deal with the signed value; the operations in this interface deal with the unsigned value or with the bit sequence.? > > - Jay > Yes. C has different types for signed and unsigned but uses the same, overloaded, operator symbol '+'. Modula-3 has one type, INTEGER, and uses different operator symbols: '+' for signed arithmetic on INTEGER and Word.Plus for unsigned, on the same type INTEGER. Word.T is not a distinct type, just a different name for INTEGER. So, to translate Modula-3 semantics into C, you have to: 1. Declare translated INTEGER/Word.T variables as either a C signed or a C unsigned type. For correct behavior, you can choose this arbitrarily, but for better human comprehension, you may want to make it depend on which name of INTEGER was used in the Modula-3 code. 2. Whenever you translate a Modula-3 '+' (always means signed), you have to first cast each operand to C signed, if it is not already so. Translating Word.Plus, (always unsigned) is symmetrical, i.e., cast each C operand to C unsigned, if not already so, before applying the operator. In either case, the C operator will be '+', which the C compiler will then de-overload (sorry for the non-word, but it's useful) to the right kind of arithmetic, as called for by the (possibly cast) C type. Of course, you have to remember the signedness of the result, and maybe eventually cast it back again, depending on how it is used in the C code. I have, on several occasions, written code the has to avoid overflows in intermediate results, work up the the ends of the range of variables, etc. It can be quite tricky, but being able to use a mix of signed, unsigned, and bit-twiddling operations without filling it full of type conversions does make it easier. Mixed operations do happen. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* M3devel on behalf of Hendrik Boom > *Sent:* Wednesday, May 26, 2021 6:06:00 PM > *To:* m3devel at elegosoft.com > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > On Wed, May 26, 2021 at 10:21:57PM +0000, Jay K wrote: >> Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. >> >> Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). > > The whole point of the Word module is that its operations alow you to get at > the machine representation of integers and handle them just the way I want. > Allowing signed and unsigned operations on them is just what I want when I use > Word.T. > > For most purposes, if I want only nonnegative integers, I use CARDINAL. > > -- hendrik > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C01a577354f074317e45308d920aba4cc%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637576743841192267%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=b%2FlADqJMXCD4OTtPW8mbDj7rtRLQ%2F0hXSMGLi%2Bj6tGw%3D&reserved=0 > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Sat May 29 02:33:34 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 29 May 2021 00:33:34 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> <20210527010600.am7fuyidv3ikqtiz@topoi.pooq.com> , Message-ID: You probably know this, but for the record..the IR has types "everywhere". On every operation. Every add, every load, and store. So it, the IR and the generated C, does not resemble: INT32 a,b,c; c = a + b; But more like: INT32 a,b,c; *(INT32*)&c = (INT32)a + (INT32)b; So I think I have the important stuff long since covered and working. Again, the system does completely build itself, gui stuff works; granted, it is possible, these semantics are not depended upon. And we probably could even do: char a[4], b[4], c[4]; *(INT32*)&c = *(INT32*)&b + *(INT32*)&b; I might try that as an experiment. It is a little gross. But hey, it could be assembly just as well. This is more readable, more portable, more debuggable. I try to remove some redundant casts. They really pile up and are often from same type to same type. But the approach is a little dumb. And it is ok. It works. What I am working on now is something kinda less interesting, but I think has some value. The generated C has declarations for called functions, implemented functions (of course), and including external functions. (It isn't K&R. :) ) And we have the C implementations hand written, and often prototypes for those too. I want them all to "agree", in a fashion, more than they do today. That is what I'm trying to get to work. Historically, Modula-3 could be quite sloppy. It matters little if you are passing UINT64 or INT64 or size_t, as long as the value is already that size before passing. But at the C level, you cannot write, e.g.: typedef unsigned long long INT64; typedef unsigned long long UINT64; typedef long size_t; // hypothetical but realistic void f(size_t); void f(UINT64); // or INT64 => error You can also not say: struct Thash { ... }; struct Date__T { /* identical ... */ }; void extern(Date__T*); void extern(Thash*); If only C had structural type equivalence. :) That is what I am trying to fix now. So I can combine all the files. One file to distribute, one to build. If not too much stress on the C compiler. I have made good progress, but it is surprisingly much. I understand that C has operator overloading, and I agree that isn't great. Worse is the mixing of types within one expression without explicit conversion. int < uint and such, most people gloss over it, no big deal, but it is actually extremely unclear as to what it means..it really just doesn't work, there's silent loss of values. But I still think, even if there is no overlap in operation names/symbols, the types should also be different. I realize that is redundant. i.e. we could just have everything be opaque and call TInt.Add, TInt.LT, TInt.Assign, etc. > you may want to make it depend on which name of INTEGER was used in the Modula-3 code Right. Some day hopefully the generated C will have local and global variables of type Word__T in place of INT64 or INTEGER or UINT64. And a shortage instead of many uses of "ADDRESS" which is void* or char*. For now I am focusing on parameters. Variables, and parameter load/stores, continue to "benefit" from the casts. > Word.T is not a distinct type, just a different name for INTEGER. I know you've all said this repeatedly but it still feels very wrong and unnatural and limiting to me. It feels really very BCPL-like or Bliss-like, where apparently memory was just all untyped words, and just had to call the correct functions to imbue meaning/type to that address. Which I realize can work, is sufficient, it just seems kinda wrong. VAR a:Word.T := -1; What should the debugger show?? This seems even more reason to focus on types. They are what debug info traffics in. You might argue, sure, debug info caters to its C users. But what would Modula-3 debuginfo even look like? Hey, we happened to pass this INTEGER to Word functions, to display it differently? I tend to back fill (go from conclusion to reason), but this does seem to further my point. - Jay ________________________________ From: Rodney M. Bates Sent: Friday, May 28, 2021 9:12 PM To: Jay K ; Hendrik Boom ; m3devel at elegosoft.com Subject: Re: [M3devel] Word.T rules vs. enforcement? On 5/26/21 8:14 PM, Jay K wrote: > Aha I looked. > Word combines multiple things. Yuck. > I thought it was just one thing: full range unsigned non-trapping integers. > > ?AWord.Twrepresents a sequence ofWord.Sizebitsw_0, ...,w_(Word.Size-1). It also represents the unsigned numberSUM (w_i * 2^i). Finally, it also represents a signedINTEGERby some implementation-dependent encoding (for example, two's complement). The built-in operations of the language deal with the signed value; the operations in this interface deal with the unsigned value or with the bit sequence.? > > - Jay > Yes. C has different types for signed and unsigned but uses the same, overloaded, operator symbol '+'. Modula-3 has one type, INTEGER, and uses different operator symbols: '+' for signed arithmetic on INTEGER and Word.Plus for unsigned, on the same type INTEGER. Word.T is not a distinct type, just a different name for INTEGER. So, to translate Modula-3 semantics into C, you have to: 1. Declare translated INTEGER/Word.T variables as either a C signed or a C unsigned type. For correct behavior, you can choose this arbitrarily, but for better human comprehension, you may want to make it depend on which name of INTEGER was used in the Modula-3 code. 2. Whenever you translate a Modula-3 '+' (always means signed), you have to first cast each operand to C signed, if it is not already so. Translating Word.Plus, (always unsigned) is symmetrical, i.e., cast each C operand to C unsigned, if not already so, before applying the operator. In either case, the C operator will be '+', which the C compiler will then de-overload (sorry for the non-word, but it's useful) to the right kind of arithmetic, as called for by the (possibly cast) C type. Of course, you have to remember the signedness of the result, and maybe eventually cast it back again, depending on how it is used in the C code. I have, on several occasions, written code the has to avoid overflows in intermediate results, work up the the ends of the range of variables, etc. It can be quite tricky, but being able to use a mix of signed, unsigned, and bit-twiddling operations without filling it full of type conversions does make it easier. Mixed operations do happen. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* M3devel on behalf of Hendrik Boom > *Sent:* Wednesday, May 26, 2021 6:06:00 PM > *To:* m3devel at elegosoft.com > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > On Wed, May 26, 2021 at 10:21:57PM +0000, Jay K wrote: >> Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. >> >> Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). > > The whole point of the Word module is that its operations alow you to get at > the machine representation of integers and handle them just the way I want. > Allowing signed and unsigned operations on them is just what I want when I use > Word.T. > > For most purposes, if I want only nonnegative integers, I use CARDINAL. > > -- hendrik > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Ca952c2a9bcfe4431e08c08d9221d509b%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637578331564289704%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=%2Fg%2FlN3HWjIIchUBh1i5%2FgCYsTpwleLy0rSik66xRabA%3D&reserved=0 > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Ca952c2a9bcfe4431e08c08d9221d509b%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637578331564289704%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=%2Fg%2FlN3HWjIIchUBh1i5%2FgCYsTpwleLy0rSik66xRabA%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Sat May 29 03:42:13 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Fri, 28 May 2021 20:42:13 -0500 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> <20210527010600.am7fuyidv3ikqtiz@topoi.pooq.com> Message-ID: Maybe I am not getting what you are saying, but it keeps sounding to me like you either don't understand or just don't like Modula-3 semantics, and are proposing not to implement them. On 5/28/21 7:33 PM, Jay K wrote: > You probably know this, but for the record..the IR has types "everywhere". On every operation. > Every add, every load, and store. > > So it, the IR and the generated C, does not resemble: > > INT32 a,b,c; > > c = a + b; > > But more like: > > ?INT32 a,b,c; > ?*(INT32*)&c = (INT32)a + (INT32)b; > > So I think I have the important stuff long since covered and working. > Again, the system does completely build itself, gui stuff works; granted, it is possible, these semantics are not depended upon. > > ?And we probably could even do: > > ?char a[4], b[4], c[4]; > ?*(INT32*)&c = *(INT32*)&b + *(INT32*)&b; > > I might try that as an experiment. > > It is a little gross. But hey, it could be assembly just as well. This is more > readable, more portable, more debuggable. > > I try to remove some redundant casts. They really pile up and are often > from same type to same type. > But the approach is a little dumb. > And it is ok. It works. > > What I am working on now is something kinda less interesting, but I think has some value. > > The generated C has declarations for called functions, implemented functions (of course), > and including external functions. (It isn't K&R. :) ) > > And we have the C implementations hand written, and often prototypes for those too. > I want them all to "agree", in a fashion, more than they do today. > That is what I'm trying to get to work. > > > Historically, Modula-3 could be quite sloppy. > > It matters little if you are passing UINT64 or INT64 or size_t, as long as the value is already that size before passing. > > But at the C level, you cannot write, e.g.: > ? typedef unsigned long long INT64; > ? typedef unsigned long long UINT64; > ? typedef long size_t; // hypothetical but realistic > > ?void f(size_t); > ?void f(UINT64); // or INT64 > > ?=> error > > ?You can also not say: > > ?struct Thash { ... }; > ?struct Date__T { /* identical ... */ }; > > ?void extern(Date__T*); > ?void extern(Thash*); > > If only C had structural type equivalence. :) > > That is what I am trying to fix now. > > So I can combine all the files. > One file to distribute, one to build. > If not too much stress on the C compiler. > > I have made good progress, but it is surprisingly much. > > I understand that C has operator overloading, and I agree that isn't great. > Worse is the mixing of types within one expression without explicit conversion. > int < uint and such, most people gloss over it, no big deal, but it is actually > extremely unclear as to what it means..it really just doesn't work, there's > silent loss of values. > > But I still think, even if there is no overlap in operation names/symbols, > the types should also be different. > I realize that is redundant. > i.e. we could just have everything be opaque and call TInt.Add, TInt.LT, TInt.Assign, etc. > > ?> you may want to make it depend on which name of INTEGER was used in the Modula-3 code > > Right. Some day hopefully the generated C will have local and global variables > of type Word__T in place of INT64 or INTEGER or UINT64. And a shortage instead > of many uses of "ADDRESS" which is void* or char*. > > For now I am focusing on parameters. > Variables, and parameter load/stores, continue to "benefit" from the casts. > > ?> Word.T is not a distinct type, just a different name for INTEGER. > > I know you've all said this repeatedly but it still feels very wrong and unnatural > and limiting to me. > It feels really very BCPL-like or Bliss-like, where apparently memory was just all > untyped words, and just had to call the correct functions to imbue meaning/type to that address. > Which I realize can work, is sufficient, it just seems kinda wrong. > > > VAR a:Word.T := -1; > > What should the debugger show?? > This seems even more reason to focus on types. They are what debug info traffics in. > You might argue, sure, debug info caters to its C users. But what would Modula-3 debuginfo even look like? > Hey, we happened to pass this INTEGER to Word functions, to display it differently? > I tend to back fill (go from conclusion to reason), but this does seem to further my point. > > ?- Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Rodney M. Bates > *Sent:* Friday, May 28, 2021 9:12 PM > *To:* Jay K ; Hendrik Boom ; m3devel at elegosoft.com > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > > > On 5/26/21 8:14 PM, Jay K wrote: >> Aha I looked. >> Word combines multiple things. Yuck. >> I thought it was just one thing: full range unsigned non-trapping integers. >> >> ?AWord.Twrepresents a sequence ofWord.Sizebitsw_0, ...,w_(Word.Size-1). It also represents the unsigned numberSUM (w_i * 2^i). Finally, it also represents a signedINTEGERby some implementation-dependent encoding (for example, two's complement). The built-in operations of the language deal with the signed value; the operations in this interface deal with the unsigned value or with the bit sequence.? >> >> - Jay >> > > Yes.? C has different types for signed and unsigned but uses the > same, overloaded, operator symbol '+'.? Modula-3 has one type, INTEGER, > and uses different operator symbols: '+' for signed arithmetic on INTEGER > and Word.Plus for unsigned, on the same type INTEGER.? Word.T is not a > distinct type, just a different name for INTEGER. > > So, to translate Modula-3 semantics into C, you have to: > > 1. Declare translated INTEGER/Word.T variables as either a C signed or > ??? a C unsigned type.? For correct behavior, you can choose this arbitrarily, > ??? but for better human comprehension, you may want to make it depend on which > ??? name of INTEGER was used in the Modula-3 code. > > 2. Whenever you translate a Modula-3 '+' (always means signed), you have > ??? to first cast each operand to C signed, if it is not already so. > ??? Translating Word.Plus, (always unsigned) is symmetrical, i.e., cast > ??? each C operand to C unsigned, if not already so, before applying the > ??? operator.? In either case, the C operator will be '+', which the C compiler > ??? will then de-overload (sorry for the non-word, but it's useful) to the > ??? right kind of arithmetic, as called for by the (possibly cast) C type. > > Of course, you have to remember the signedness of the result, and maybe > eventually cast it back again, depending on how it is used in the C code. > > I have, on several occasions, written code the has to avoid overflows in > intermediate results, work up the the ends of the range of variables, etc. > It can be quite tricky, but being able to use a mix of signed, unsigned, > and bit-twiddling operations without filling it full of type conversions > does make it easier.? Mixed operations do happen. > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >> *From:* M3devel on behalf of Hendrik Boom >> *Sent:* Wednesday, May 26, 2021 6:06:00 PM >> *To:* m3devel at elegosoft.com >> *Subject:* Re: [M3devel] Word.T rules vs. enforcement? >> On Wed, May 26, 2021 at 10:21:57PM +0000, Jay K wrote: >>> Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. >>> >>> Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). >> >> The whole point of the Word module is that its operations alow you to get at >> the machine representation of integers and handle them just the way I want. >> Allowing signed and unsigned operations on them is just what I want when I use >> Word.T. >> >> For most purposes, if I want only nonnegative integers, I use CARDINAL. >> >> -- hendrik >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Ca952c2a9bcfe4431e08c08d9221d509b%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637578331564289704%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=%2Fg%2FlN3HWjIIchUBh1i5%2FgCYsTpwleLy0rSik66xRabA%3D&reserved=0 > > >> >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Ca952c2a9bcfe4431e08c08d9221d509b%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637578331564289704%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=%2Fg%2FlN3HWjIIchUBh1i5%2FgCYsTpwleLy0rSik66xRabA%3D&reserved=0 >> > > -- > Rodney Bates > rodney.m.bates at acm.org > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Sat May 29 04:19:23 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 29 May 2021 02:19:23 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> <20210527010600.am7fuyidv3ikqtiz@topoi.pooq.com> , Message-ID: I don?t love them, in this case, but it isn?t a big deal. It seems ok otherwise and I believe I have implemented them. The IR often casts from same type to same type. Those I remove some of. Actual type-changing casts are kept. I need the prototypes to match with high fidelity, but that doesn?t actually change semantics. The IR operations are deeply typed and ignore the declared types of values. The function prototypes have been wrong all along, for all backends. And you cannot fix them. You cannot really declare an extern function that takes a size_t. I find that disappointing. But you can say a function takes an integer which is almost the same thing. Modula3 is like assembly in this respect, more than is C. Typed operations instead of typed values. Relatively few systems work this way, I think. But regarding integers, even if unsigned variations are common, systems do struggle to do even better, like having distinct ?inches? or ?meters? types. You end up often with like typedef int Inches_t; typedef int Meters_t; Inches_t i; Meters_t m; i + m; Same problem. It ought not compile but it does. Some languages address this problem directly and well. C and C++ do not. Modula3 does not, except in that, ?there is no problem?. Most people would view this as a static type error and hope for a compilation error. It isn?t the semantics per se that are the problem, but their expression. That variables are so weakly typed and that types really flow from operations. It is like having a loophole that goes unstated. - Jay ________________________________ From: Rodney M. Bates Sent: Friday, May 28, 2021 6:42:13 PM To: Jay K ; Hendrik Boom ; m3devel at elegosoft.com ; rodney.m.bates at acm.org Subject: Re: [M3devel] Word.T rules vs. enforcement? Maybe I am not getting what you are saying, but it keeps sounding to me like you either don't understand or just don't like Modula-3 semantics, and are proposing not to implement them. On 5/28/21 7:33 PM, Jay K wrote: > You probably know this, but for the record..the IR has types "everywhere". On every operation. > Every add, every load, and store. > > So it, the IR and the generated C, does not resemble: > > INT32 a,b,c; > > c = a + b; > > But more like: > > INT32 a,b,c; > *(INT32*)&c = (INT32)a + (INT32)b; > > So I think I have the important stuff long since covered and working. > Again, the system does completely build itself, gui stuff works; granted, it is possible, these semantics are not depended upon. > > And we probably could even do: > > char a[4], b[4], c[4]; > *(INT32*)&c = *(INT32*)&b + *(INT32*)&b; > > I might try that as an experiment. > > It is a little gross. But hey, it could be assembly just as well. This is more > readable, more portable, more debuggable. > > I try to remove some redundant casts. They really pile up and are often > from same type to same type. > But the approach is a little dumb. > And it is ok. It works. > > What I am working on now is something kinda less interesting, but I think has some value. > > The generated C has declarations for called functions, implemented functions (of course), > and including external functions. (It isn't K&R. :) ) > > And we have the C implementations hand written, and often prototypes for those too. > I want them all to "agree", in a fashion, more than they do today. > That is what I'm trying to get to work. > > > Historically, Modula-3 could be quite sloppy. > > It matters little if you are passing UINT64 or INT64 or size_t, as long as the value is already that size before passing. > > But at the C level, you cannot write, e.g.: > typedef unsigned long long INT64; > typedef unsigned long long UINT64; > typedef long size_t; // hypothetical but realistic > > void f(size_t); > void f(UINT64); // or INT64 > > => error > > You can also not say: > > struct Thash { ... }; > struct Date__T { /* identical ... */ }; > > void extern(Date__T*); > void extern(Thash*); > > If only C had structural type equivalence. :) > > That is what I am trying to fix now. > > So I can combine all the files. > One file to distribute, one to build. > If not too much stress on the C compiler. > > I have made good progress, but it is surprisingly much. > > I understand that C has operator overloading, and I agree that isn't great. > Worse is the mixing of types within one expression without explicit conversion. > int < uint and such, most people gloss over it, no big deal, but it is actually > extremely unclear as to what it means..it really just doesn't work, there's > silent loss of values. > > But I still think, even if there is no overlap in operation names/symbols, > the types should also be different. > I realize that is redundant. > i.e. we could just have everything be opaque and call TInt.Add, TInt.LT, TInt.Assign, etc. > > > you may want to make it depend on which name of INTEGER was used in the Modula-3 code > > Right. Some day hopefully the generated C will have local and global variables > of type Word__T in place of INT64 or INTEGER or UINT64. And a shortage instead > of many uses of "ADDRESS" which is void* or char*. > > For now I am focusing on parameters. > Variables, and parameter load/stores, continue to "benefit" from the casts. > > > Word.T is not a distinct type, just a different name for INTEGER. > > I know you've all said this repeatedly but it still feels very wrong and unnatural > and limiting to me. > It feels really very BCPL-like or Bliss-like, where apparently memory was just all > untyped words, and just had to call the correct functions to imbue meaning/type to that address. > Which I realize can work, is sufficient, it just seems kinda wrong. > > > VAR a:Word.T := -1; > > What should the debugger show?? > This seems even more reason to focus on types. They are what debug info traffics in. > You might argue, sure, debug info caters to its C users. But what would Modula-3 debuginfo even look like? > Hey, we happened to pass this INTEGER to Word functions, to display it differently? > I tend to back fill (go from conclusion to reason), but this does seem to further my point. > > - Jay > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ > *From:* Rodney M. Bates > *Sent:* Friday, May 28, 2021 9:12 PM > *To:* Jay K ; Hendrik Boom ; m3devel at elegosoft.com > *Subject:* Re: [M3devel] Word.T rules vs. enforcement? > > > On 5/26/21 8:14 PM, Jay K wrote: >> Aha I looked. >> Word combines multiple things. Yuck. >> I thought it was just one thing: full range unsigned non-trapping integers. >> >> ?AWord.Twrepresents a sequence ofWord.Sizebitsw_0, ...,w_(Word.Size-1). It also represents the unsigned numberSUM (w_i * 2^i). Finally, it also represents a signedINTEGERby some implementation-dependent encoding (for example, two's complement). The built-in operations of the language deal with the signed value; the operations in this interface deal with the unsigned value or with the bit sequence.? >> >> - Jay >> > > Yes. C has different types for signed and unsigned but uses the > same, overloaded, operator symbol '+'. Modula-3 has one type, INTEGER, > and uses different operator symbols: '+' for signed arithmetic on INTEGER > and Word.Plus for unsigned, on the same type INTEGER. Word.T is not a > distinct type, just a different name for INTEGER. > > So, to translate Modula-3 semantics into C, you have to: > > 1. Declare translated INTEGER/Word.T variables as either a C signed or > a C unsigned type. For correct behavior, you can choose this arbitrarily, > but for better human comprehension, you may want to make it depend on which > name of INTEGER was used in the Modula-3 code. > > 2. Whenever you translate a Modula-3 '+' (always means signed), you have > to first cast each operand to C signed, if it is not already so. > Translating Word.Plus, (always unsigned) is symmetrical, i.e., cast > each C operand to C unsigned, if not already so, before applying the > operator. In either case, the C operator will be '+', which the C compiler > will then de-overload (sorry for the non-word, but it's useful) to the > right kind of arithmetic, as called for by the (possibly cast) C type. > > Of course, you have to remember the signedness of the result, and maybe > eventually cast it back again, depending on how it is used in the C code. > > I have, on several occasions, written code the has to avoid overflows in > intermediate results, work up the the ends of the range of variables, etc. > It can be quite tricky, but being able to use a mix of signed, unsigned, > and bit-twiddling operations without filling it full of type conversions > does make it easier. Mixed operations do happen. > > ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ >> *From:* M3devel on behalf of Hendrik Boom >> *Sent:* Wednesday, May 26, 2021 6:06:00 PM >> *To:* m3devel at elegosoft.com >> *Subject:* Re: [M3devel] Word.T rules vs. enforcement? >> On Wed, May 26, 2021 at 10:21:57PM +0000, Jay K wrote: >>> Modules are ?just? a namespace mechanism and not a complete solution on their own, to enable some/enough level of static validation by a compiler. >>> >>> Modula3 despite not being called Types3 does have a significant type system, that does help a lot with static validation. I just want a little more. I think replacing Word.T with a record with the same size as an integer would go far (not really a language change). >> >> The whole point of the Word module is that its operations alow you to get at >> the machine representation of integers and handle them just the way I want. >> Allowing signed and unsigned operations on them is just what I want when I use >> Word.T. >> >> For most purposes, if I want only nonnegative integers, I use CARDINAL. >> >> -- hendrik >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C722b8ab31e014695928008d92243062a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637578493533580201%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=b%2FcJBfg2bkH4swMcfE4J8gF76jP2kisY7GSmOOaigQ0%3D&reserved=0 > > >> >> _______________________________________________ >> M3devel mailing list >> M3devel at elegosoft.com >> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C722b8ab31e014695928008d92243062a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637578493533600195%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=CKrcLTUun8TjjE2NFFcg%2BV9d%2BRzfoHGjJKuDjao99tM%3D&reserved=0 >> > > -- > Rodney Bates > rodney.m.bates at acm.org > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C722b8ab31e014695928008d92243062a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637578493533600195%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=CKrcLTUun8TjjE2NFFcg%2BV9d%2BRzfoHGjJKuDjao99tM%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From hendrik at topoi.pooq.com Sun May 30 01:28:44 2021 From: hendrik at topoi.pooq.com (Hendrik Boom) Date: Sat, 29 May 2021 19:28:44 -0400 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> <20210527010600.am7fuyidv3ikqtiz@topoi.pooq.com> Message-ID: <20210529232844.v5sxig6gm52jqy2o@topoi.pooq.com> On Sat, May 29, 2021 at 12:33:34AM +0000, Jay K wrote: > You probably know this, but for the record..the IR has types "everywhere". On every operation. > Every add, every load, and store. > > So it, the IR and the generated C, does not resemble: > > INT32 a,b,c; > > c = a + b; > > But more like: > > INT32 a,b,c; > *(INT32*)&c = (INT32)a + (INT32)b; > > So I think I have the important stuff long since covered and working. > Again, the system does completely build itself, gui stuff works; granted, it is possible, these semantics are not depended upon. > > And we probably could even do: > > char a[4], b[4], c[4]; > *(INT32*)&c = *(INT32*)&b + *(INT32*)&b; THis might give alignment problems on some machines. -- hendrik From jayk123 at hotmail.com Sun May 30 01:44:27 2021 From: jayk123 at hotmail.com (Jay K) Date: Sat, 29 May 2021 23:44:27 +0000 Subject: [M3devel] Word.T rules vs. enforcement? In-Reply-To: <20210529232844.v5sxig6gm52jqy2o@topoi.pooq.com> References: <4d44fe6d-8527-c94d-4727-54e35c00fb38@lcwb.coop> <20210527010600.am7fuyidv3ikqtiz@topoi.pooq.com> , <20210529232844.v5sxig6gm52jqy2o@topoi.pooq.com> Message-ID: > THis might give alignment > problems on some machines Correct, of course, thank you for reminder. The actual system is ok though. I surely had it running on Sparc, and will double check that soon. I even handle struct alignment in a funny adequate way. - Jay ________________________________ From: M3devel on behalf of Hendrik Boom Sent: Saturday, May 29, 2021 4:28:44 PM To: m3devel Subject: Re: [M3devel] Word.T rules vs. enforcement? On Sat, May 29, 2021 at 12:33:34AM +0000, Jay K wrote: > You probably know this, but for the record..the IR has types "everywhere". On every operation. > Every add, every load, and store. > > So it, the IR and the generated C, does not resemble: > > INT32 a,b,c; > > c = a + b; > > But more like: > > INT32 a,b,c; > *(INT32*)&c = (INT32)a + (INT32)b; > > So I think I have the important stuff long since covered and working. > Again, the system does completely build itself, gui stuff works; granted, it is possible, these semantics are not depended upon. > > And we probably could even do: > > char a[4], b[4], c[4]; > *(INT32*)&c = *(INT32*)&b + *(INT32*)&b; THis might give alignment problems on some machines. -- hendrik _______________________________________________ M3devel mailing list M3devel at elegosoft.com https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7C851d3f50a3b14bcc218f08d922f98d84%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637579277482053943%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=pH2HvZMf0wZHMzKzHKF3MrGyr7GHuELveXPIX64Ks5o%3D&reserved=0 -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sun May 30 04:20:42 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 30 May 2021 02:20:42 +0000 Subject: [M3devel] better interface to backend to send types? Message-ID: I think we need a better interface to backends, in terms of relaying type information. You know, when you think of hashes in software, roughly two things come to mind. - Hash tables. The hash is a heuristic but typically strong, partition of the data. Collisions are not desirable, but ok. Full values are compared upon hash collision. Such hashes are typically 32bit or 64bits. (typically the 64bit hash is mod'ed with a 32bit bucket count, but keeping the full 64bit hash in the table entry speeds up equality checks upon collision) - Cryptographic hashes. These are larger, slower to compute, and carefully designed to be collision resistant. Though I'm not sure of the guarantees. They aren't usually, I think, used in place of a message, but along with a message. Modula-3 (m3front/m3back) has a third form that I find unusual. It uses a not particularly strong hash, like a hashtable, but does not deal with collisions. I strikes me, multiple possibilities: - Come up with a longer, likely arbitrarily long, encoding of a type. A "string". Or an opaque blob. - Move one or more backends into m3front and pass m3front's Type. Upon hash collision, use Type.IsEqual. Like a normal hash table. ? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Sun May 30 21:06:25 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 30 May 2021 19:06:25 +0000 Subject: [M3devel] socket SO_LINGER Win32 vs. Posix.. In-Reply-To: References: Message-ID: Anyone else want to keep or remove this TCP_NODELAY setting? It cannot be correct, doing opposite things on Windows and Unix. - Jay ________________________________ From: Jay K Sent: Sunday, March 7, 2021 8:54 AM To: m3devel Subject: socket SO_LINGER Win32 vs. Posix.. We have opposite looking code on Posix and Win32: C:\s\cm3\m3-comm\tcp\src\POSIX\TCP.m3 PROCEDURE InitFD(fd: CARDINAL) = (* We assume that the runtime ignores SIGPIPE signals *) VAR one: int := 1; linger := Usocket.struct_linger{1, 1}; <== Specifically this. BEGIN EVAL Usocket.setsockopt(fd, Usocket.SOL_SOCKET, Usocket.SO_LINGER, ADR(linger), BYTESIZE(linger)); EVAL Usocket.setsockopt( fd, Uin.IPPROTO_TCP, TCP_NODELAY, ADR(one), BYTESIZE(one)); MakeNonBlocking (fd); END InitFD; C:\s\cm3\m3-comm\tcp\src\WIN32\TCP.m3 PROCEDURE InitSock(sock: WinSock.SOCKET) = (* We assume that the runtime ignores SIGPIPE signals *) VAR one : BOOL := 1; linger := WinSock.struct_linger{0, 0}; <== Specifically this. BEGIN EVAL WinSock.setsockopt( sock, WinSock.SOL_SOCKET, WinSock.SO_LINGER, ADR(linger), BYTESIZE(linger)); EVAL WinSock.setsockopt( sock, WinSock.IPPROTO_TCP, WinSock.TCP_NODELAY, ADR(one), BYTESIZE(one)); IF WinSock.ioctlsocket(sock, WinSock.FIONBIO, ADR(one)) = SockErr THEN IPError.Die(); END; END InitSock; Ignoring the FIONBIO / MakeNonBlocking part, I think the right thing here is to delete both SO_LINGER settings, and probably TCP_NODELAY. The principles would be: - Accept defaults. - Let higher levels decide. - And do not have opposite behavior on Windows and Unix. Agreed? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From rodney_bates at lcwb.coop Sun May 30 23:03:58 2021 From: rodney_bates at lcwb.coop (Rodney M. Bates) Date: Sun, 30 May 2021 16:03:58 -0500 Subject: [M3devel] expand typeids to 64bits? In-Reply-To: References: Message-ID: On 5/27/21 8:30 PM, Jay K wrote: > Should we expand typeids to 64bits? I think this is a good idea, in light of the recently discovered collision of 32-bit UIDs. I don't know how good a hash the 64-bit fingerprints are, but from reading the comments some time ago, it sounded like at least some good effort went into its algorithm. Just using it instead of XORing its halves should greatly reduce the likelihood of collisions. Changes would be pervasive, but maybe just changing some declarations would take care of a lot of it. It could be stored in a LONGINT, so would easily work the same on 32-bit and 64-bit machines without extra effort. Pickles would definitely be affected. For sure, modified pickle code should at least detect a current pickle file. With that once done, it should not be hard to make it adapt and read either current or new pickle files. Also network objects would be affected, which, as I recall, use pickles, but may still need some additional glue work. > > To allow for a better hash or encoding function? i.e. fewer collisions, or encode some data directly, like size? > > I assume memory usage is ok. If 32bits weren't too much 25 years ago, 64 bits are probably ok today. Memories have far more than doubled.. > > Do we need pickle compatibilty? That'd seem a big reason why not. > > ?- Jay > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://m3lists.elegosoft.com/mailman/listinfo/m3devel > -- Rodney Bates rodney.m.bates at acm.org From jayk123 at hotmail.com Mon May 31 00:52:18 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 30 May 2021 22:52:18 +0000 Subject: [M3devel] expand typeids to 64bits? In-Reply-To: References: , Message-ID: Um, I think now, not sure, that typeids have no runtime bearing. Only fingerprints do. We could buy time by switching to fingerprints and ignoring their collisions. I would like the compiler or something to do a better/at-all check for collisions. For existing typeids that should be easy, then make a program/module that fails to compile, then verify switching to fingerprints does not fail. The program could be more than necessary -- not just with the two known types that collide, but collect all types in the tree into one program (modulo any name collisions, perhaps). (Or two programs I guess, small with just the two, large with all). Given the semi-LTCG nature of Modula-3 already, this be a "fun" exercise, a medium not huge amount of work.. ? - Jay ________________________________ From: Rodney M. Bates Sent: Sunday, May 30, 2021 9:03 PM To: Jay K ; m3devel Subject: Re: [M3devel] expand typeids to 64bits? On 5/27/21 8:30 PM, Jay K wrote: > Should we expand typeids to 64bits? I think this is a good idea, in light of the recently discovered collision of 32-bit UIDs. I don't know how good a hash the 64-bit fingerprints are, but from reading the comments some time ago, it sounded like at least some good effort went into its algorithm. Just using it instead of XORing its halves should greatly reduce the likelihood of collisions. Changes would be pervasive, but maybe just changing some declarations would take care of a lot of it. It could be stored in a LONGINT, so would easily work the same on 32-bit and 64-bit machines without extra effort. Pickles would definitely be affected. For sure, modified pickle code should at least detect a current pickle file. With that once done, it should not be hard to make it adapt and read either current or new pickle files. Also network objects would be affected, which, as I recall, use pickles, but may still need some additional glue work. > > To allow for a better hash or encoding function? i.e. fewer collisions, or encode some data directly, like size? > > I assume memory usage is ok. If 32bits weren't too much 25 years ago, 64 bits are probably ok today. Memories have far more than doubled.. > > Do we need pickle compatibilty? That'd seem a big reason why not. > > - Jay > > _______________________________________________ > M3devel mailing list > M3devel at elegosoft.com > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fm3lists.elegosoft.com%2Fmailman%2Flistinfo%2Fm3devel&data=04%7C01%7C%7Ce60c948be1c646e4836a08d923ae79d1%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637580054540579763%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=6BW3TJeMg2Ah2P1CDShsyG0wkt5D7NPErxU3lB%2BY18s%3D&reserved=0 > -- Rodney Bates rodney.m.bates at acm.org -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 31 01:11:14 2021 From: jayk123 at hotmail.com (Jay K) Date: Sun, 30 May 2021 23:11:14 +0000 Subject: [M3devel] Reducing types used in C interop? Message-ID: In places "like" m3cores/src/Unix we have many integer type names in use. unsigned, unsigned_short, socklen_t, size_t, etc. Some of them do not behave as they might sound. i.e. on 32bit platforms, "unsigned" is not unsigned. Some of them are chosen to match the underlying platform. Many occurences of "int" do. Some are chosen to have one type that "works" across all platforms. With possible runtime failures if you go past a specific platform's capabilities. Usocket.socklen_t for example. While I am not suggesting removing any of the type definitions, there is a part of me that thinks that many integer types only make things more confusing. The Google style guide for example, advocates mostly just to use "int". Mostly. But also to check or assert against negative numbers. I am not sure I like this, but it seems to work well for them. I had been raised to believe, that other than floating point numbers, negative numbers also do not have much use in systems programming. But again, perhaps one should reduce the type population? With that approach in mind, maybe we should just use INTEGER mostly? I understand, file sizes should be LONGINT, or LONGCARD. I understand array indices could be CARDINAL. I understand there is usually some reason to motivate every integer type's existance and use. But I am not sure it is worth it. Maybe INTEGER is really pretty ok just about everywhere? Also I think about calling conventions. A function that takes two integers is going to use two registers. There is no packing of values into sub-registers, typically. And if there is, it also doesn't seem very worthwhile. For arrays or structs, sure, save space. For locals, worth it? Some of the types do imply range checks on the Modula-3 side. That is a reason maybe to leave things as they are. You know, while carefully picking a matching or adequate type seems on one hand like commendable attention to detail, maybe it is overly obsessive and does not make things better? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 31 05:10:23 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 31 May 2021 03:10:23 +0000 Subject: [M3devel] Reducing types used in C interop? In-Reply-To: References: Message-ID: Well I started to do this, and maybe just not worth much, or maybe better asis. It is wierd though, the position we are in passing ?size_t?. Though being precise like that, on the name, might ease very esoteric systems..on VMS the sizes of pointers, uintptr_t, and size_t do not follow the informal convention of being exactly equal (uintptr_t is often larger than a pointer, size_t often smaller than a pointer). - Jay. - Jay ________________________________ From: M3devel on behalf of Jay K Sent: Sunday, May 30, 2021 4:11:14 PM To: m3devel Subject: [M3devel] Reducing types used in C interop? In places "like" m3cores/src/Unix we have many integer type names in use. unsigned, unsigned_short, socklen_t, size_t, etc. Some of them do not behave as they might sound. i.e. on 32bit platforms, "unsigned" is not unsigned. Some of them are chosen to match the underlying platform. Many occurences of "int" do. Some are chosen to have one type that "works" across all platforms. With possible runtime failures if you go past a specific platform's capabilities. Usocket.socklen_t for example. While I am not suggesting removing any of the type definitions, there is a part of me that thinks that many integer types only make things more confusing. The Google style guide for example, advocates mostly just to use "int". Mostly. But also to check or assert against negative numbers. I am not sure I like this, but it seems to work well for them. I had been raised to believe, that other than floating point numbers, negative numbers also do not have much use in systems programming. But again, perhaps one should reduce the type population? With that approach in mind, maybe we should just use INTEGER mostly? I understand, file sizes should be LONGINT, or LONGCARD. I understand array indices could be CARDINAL. I understand there is usually some reason to motivate every integer type's existance and use. But I am not sure it is worth it. Maybe INTEGER is really pretty ok just about everywhere? Also I think about calling conventions. A function that takes two integers is going to use two registers. There is no packing of values into sub-registers, typically. And if there is, it also doesn't seem very worthwhile. For arrays or structs, sure, save space. For locals, worth it? Some of the types do imply range checks on the Modula-3 side. That is a reason maybe to leave things as they are. You know, while carefully picking a matching or adequate type seems on one hand like commendable attention to detail, maybe it is overly obsessive and does not make things better? - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: From jayk123 at hotmail.com Mon May 31 10:34:58 2021 From: jayk123 at hotmail.com (Jay K) Date: Mon, 31 May 2021 08:34:58 +0000 Subject: [M3devel] m3cg.vars/procs across multiple passes, var param vs. return? Message-ID: In the M3CG system, some functions return private optional state: M3CG.Var, M3CG.Proc that is later passed to other functions. I have a feeling, for the sake of multipass backends that iterate over the implied IR, as M3CG_MultiPass provides, this should really be a VAR parameter that backends can decide to treat as in/out or out, or out once and then in thereafter (i.e. initialized to nil for first call, and then later calls initialized with result of earlier calls). i.e. so that in the face of multiple passes, the state either flows across passes, or passes are independent. And that once import_global produces an internal var, later calls to import_global can share the state. I might try to change this, but it isn't a big deal I guess. There is already op_index which allows a fair amount of convenient communication across passes, even for operations that do not return anything. It is cleanest to change this for all backends, rather than just the multipass one. But I'll try to leave it alone "for now".. - Jay -------------- next part -------------- An HTML attachment was scrubbed... URL: