Peter N Lewis wrote:
Peter N Lewis wrote:
CW takes the approach that any unknown type identifier it sees in a type definition is pointer sized (a pointer or object) and must be defined by the end of the type block.
This would be wrong, as pointer targets can (according to standard Pascal and all other dialects) be of any type and may be forward-referenced, e.g. in this example, b is probably not pointer-sized, but it must work:
program Foo;
type a = ^b; b = array [1 .. 1000] of Integer;
begin end.
Apparently CW does it the other way around, allowing the pointer itself (rather than its target) to be forward referenced. I find this rather strange, since it deviates from the standard without providing added functionality for pointer types. (Anything you can do with one, you can do with the other, by reordering declarations.)
AFAIU objects are not allowed in nested scopes, but pointers still make sense.
Correct. As near as I can tell, it removes the keyword "object" once you leave the main unit/program scope. Once in a nested scope, this:
procedure Test4; type ObjectBase = object function Doit: Integer; end; begin end;
gives an error "; expected" at "function" because object is no longer recognized as being a keyword, and so is assumed to be a forward pointer type definition.
Same for BP and GPC. Though the message is different, as `object' is still recognized as a keyword, just not allowed there. But the effect is the same, this is not accepted.
No, they are not obeyed. This compiles:
procedure Test5; type ch = Integer; Integer = Char; var c: ch; d: Integer; begin c := 42; d := 'a'; end;
Please note, I was not in any way suggesting that GPC implement this technique.
Thank you. :-)
Only explaining CW's solution to the problem, which does have a degree of elegant simplicity,
But it's also confusing (both to read, and when making changes such as reordering declarations).
But I agree, in practice with non-reference objects, the issue is a lot less prevalent.
Actually, once we have procedural types in the language forward type declarations could be usefull also for records, functions and procedures:
type automaton = function (input) : automaton;
would be a reasonable declaration for a function implementing a single step transition of a finite state machine (returning a new transition function, for the next step).
Agreed, but once you step away from the "forward definitions for pointer sized types only" policy, you get in to trouble with the types only being allowed in certain uses (primarily, not as fields for records, unless you are going to go back and recalculate all the record locations afterwards, which I guess would be doable).
Calculating offsets isn't really the only thing that needs to be done. Changing the type from a pointer to an integer afterwards would also be a major change. (The size may be the main issue from an assembler perspective, but an HLL is really more than that.)
Of course, much is doable, and there are other languages that allow quite some things (and often require multi-pass compilers to support them). Pascal chose a simple model that works without re-layouting and with single-pass compilers, and (AFAICS) covers all needs that existed in the basic language. It's simple because, although the forward-referenced type can be any type, the only type that can be used in advance is the pointer type, which is of a known kind.
Reference types present new needs. For reference-objects, OOE proposed a new solution (`class .. end'), other dialects did the same (though with different syntax, `class;' and `object; forward;'). Again here, the forward type is known to be a reference type.
Procedural (reference) types (BP extension) are another such case, which can be useful as Waldek pointed out. And probably one could also think of a case with several mutually dependent procedural types. I thought of using procedure pointers (GPC extension), but AFAICS, it's not possible (ATM) to forward-declare them either, or rather to resolve such a forward declaration, since in:
type foo = ^bar; bar = procedure;
bar would be taken to be a procedure reference, so foo would become a pointer to a reference (as required by BP compatibilty).
Actually, I just tried to figure out what BP does. It seems to be buggy. `{$T+}' is supposed to enforce strict type-checking with the address operator (@), but in this case is seems to allow the wrong assignment, and reject the right one. Anyway, bar really seems to be a pointer to a reference, so it would not help us here.
{$T-}
program Baz;
type foo = ^bar; bar = procedure (a:Integer);
procedure p (a: Integer); far; begin WriteLn (a) end;
var a: foo; b: bar;
{ So see which addresses are actually stored in a and b, not affected by the particular `@' semantics for procedural types. } ai: LongInt absolute a; bi: LongInt absolute b;
begin { For comparison } WriteLn ('Address of p: ', LongInt (@p)); WriteLn ('Address of b: ', LongInt (@bi));
{ Make `b' a reference to the procedure. Works as expected. } b := p; WriteLn (bi); b (1);
{ Make `a' a pointer to the reference to the procedure. Should work. } a := @@b; { Doesn't work with $T+ } WriteLn (ai); a^ (2); { Works! }
{ Make `a' a pointer to the procedure. Should not work. } a := @p; { Does work, even with $T+ } WriteLn (ai); a^ (2); { But this crashes! } end.
It requires the `class' keyword. There is a problem, that `class' keyword is (ATM) not accepted in Mac Pascal mode. It would be easy to implement syntax with `object' as an alternate keword,
As Gale wrote, the Mac Pascal syntax seems to be `object; forward;'. So if you can add this without conflicts, this would be preferable, I think.
but than we would have to decide what to do if `object' is used in a forward declaration in one mode, but the proper declaration uses different mode.
This should be an error IMHO. Why should we allow such useless obscurities?
Frank