At 9:07 +0200 6/7/05, Marco van de Voort wrote:
marcov@stack.nl wrote:
Delphi supports this: (remember its object model is similar)
Type MyObjectB = class; MyObjectA = class;
This seems to be what `class .. end' is in OOE.
Must be in one typeblock though, IOW, the same system as pointers.
Yes, same in OOE.
Does CW work like OOE in this regard?
No, CW does not accept either syntax:
MyObject = object; nor MyObject = object .. end;
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.
type rec = record a: UndefinedA; b: UndefinedB; end; UndefinedA = ^Integer; UndefinedB = object end;
Note that UndefinedA = UInt32 is not legal, it must be a pointer or object.
It's actually not a bad methodology and makes it easy to write data structures that is self or mutually referential, but it does not add anything over pre defining pointers or object names as EP/Delphi.
Note that afaik there is no solution for BP style objects, and actually you don't really need it, since in practice you always work with references (explicit pointers).
There is still this case:
type proc = procedure ( var o: MyObject ); MyObject = object f: proc; end;
But I agree, in practice with non-reference objects, the issue is a lot less prevalent.
Enjoy, Peter.
Peter N Lewis wrote:
At 9:07 +0200 6/7/05, Marco van de Voort wrote:
marcov@stack.nl wrote:
Delphi supports this: (remember its object model is similar)
Type MyObjectB = class; MyObjectA = class;
This seems to be what `class .. end' is in OOE.
Must be in one typeblock though, IOW, the same system as pointers.
Yes, same in OOE.
Does CW work like OOE in this regard?
No, CW does not accept either syntax:
MyObject = object; nor MyObject = object .. end;
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.
type rec = record a: UndefinedA; b: UndefinedB; end; UndefinedA = ^Integer; UndefinedB = object end;
Note that UndefinedA = UInt32 is not legal, it must be a pointer or object.
It's actually not a bad methodology and makes it easy to write data structures that is self or mutually referential, but it does not add anything over pre defining pointers or object names as EP/Delphi.
Does it allow the same in nested scopes? AFAIU objects are not allowed in nested scopes, but pointers still make sense. Also, what happens if the name is a predefined type (or a name declared in outer scope)? Normal Pascal rules are that the new definition should be used (even if the definition follows use), are they obeyed?
Note that afaik there is no solution for BP style objects, and actually you don't really need it, since in practice you always work with references (explicit pointers).
There is still this case:
type proc = procedure ( var o: MyObject ); MyObject = object f: proc; end;
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).
ATM I have implemented OOP forward declaration:
type o = class .. 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, 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.
Adding some other explicit forward declaration does not look very hard. OTOH allowing use befor definition for "pointer contexts" looks much harder.
The patch follows:
diff -u p.nn/declarations.c p/declarations.c --- p.nn/declarations.c Sun Jul 3 02:30:53 2005 +++ p/declarations.c Wed Jul 6 20:03:09 2005 @@ -3084,6 +3084,27 @@ return decl; }
+void +patch_type (tree type, tree otype) +{ + tree fwdtype = TYPE_MAIN_VARIANT (otype); + for (; fwdtype; fwdtype = TYPE_NEXT_VARIANT (fwdtype)) + { + tree t, new_variant = p_build_type_variant (type, TYPE_READONLY (fwdtype), + TYPE_VOLATILE (fwdtype)); + if (new_variant == type && fwdtype != otype) + new_variant = build_type_copy (new_variant); + if (TYPE_POINTER_TO (fwdtype)) + for (t = TYPE_MAIN_VARIANT (TYPE_POINTER_TO (fwdtype)); t; t = TYPE_NEXT_VARIANT (t)) + TREE_TYPE (t) = new_variant; + if (TYPE_REFERENCE_TO (fwdtype)) + for (t = TYPE_MAIN_VARIANT (TYPE_REFERENCE_TO (fwdtype)); t; t = TYPE_NEXT_VARIANT (t)) + TREE_TYPE (t) = new_variant; + TYPE_POINTER_TO (new_variant) = TYPE_POINTER_TO (fwdtype); + TYPE_REFERENCE_TO (new_variant) = TYPE_REFERENCE_TO (fwdtype); + } +} + /* Actually declare the types at the end of a type definition part. Resolve any forward types using existing types. */ void @@ -3093,22 +3114,51 @@
/* Resolve forward types */ for (scan = current_type_list; scan; scan = TREE_CHAIN (scan)) - if (TREE_PURPOSE (scan) && TREE_CODE (TREE_TYPE (TREE_PURPOSE (scan))) == LANG_TYPE) + if (TREE_PURPOSE (scan) && (TREE_CODE (TREE_TYPE (TREE_PURPOSE (scan))) == LANG_TYPE + || TREE_CODE (TREE_TYPE (TREE_PURPOSE (scan))) == POINTER_TYPE + && PASCAL_TYPE_CLASS (TREE_TYPE (TREE_PURPOSE (scan))) + && TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_PURPOSE (scan)))) == LANG_TYPE)) { - tree decl = lookup_name (TREE_VALUE (scan)), fwdtype, type; + tree otype = TREE_TYPE (TREE_PURPOSE (scan)); + tree decl = lookup_name (TREE_VALUE (scan)), type; + int is_class = TREE_CODE (otype) == POINTER_TYPE; if (decl && TREE_CODE (decl) == TYPE_DECL) - type = TREE_TYPE (decl); - else + { + type = TREE_TYPE (decl); + if (is_class) + if (TREE_CODE (type) == POINTER_TYPE && PASCAL_TYPE_CLASS (type)) + { + type = TREE_TYPE (type); + error ("useless forward class declaration for `%s'", + IDENTIFIER_NAME (TREE_VALUE (scan))); + } + else + { + error ("forward class %s' redefined as non-class", + IDENTIFIER_NAME (TREE_VALUE (scan))); + type = void_type_node; + } + } + else if (is_class) + { + error ("unresolved forward class `%s'", + IDENTIFIER_NAME (TREE_VALUE (scan))); + type = void_type_node; + } + else { error ("forward referenced type `%s' undefined", IDENTIFIER_NAME (TREE_VALUE (scan))); type = void_type_node; /* dwarf2out.c doesn't like error_mark_node */ } /* Patch all variants. */ - for (fwdtype = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_PURPOSE (scan))); + if (is_class) + otype = TREE_TYPE (otype); +#if 0 + for (fwdtype = TYPE_MAIN_VARIANT (otype); fwdtype; fwdtype = TYPE_NEXT_VARIANT (fwdtype)) { tree t, new_variant = p_build_type_variant (type, TYPE_READONLY (fwdtype), TYPE_VOLATILE (fwdtype)); - if (new_variant == type && fwdtype != TREE_TYPE (TREE_PURPOSE (scan))) + if (new_variant == type && fwdtype != otype) new_variant = build_type_copy (new_variant); if (TYPE_POINTER_TO (fwdtype)) for (t = TYPE_MAIN_VARIANT (TYPE_POINTER_TO (fwdtype)); t; t = TYPE_NEXT_VARIANT (t)) @@ -3119,6 +3169,9 @@ TYPE_POINTER_TO (new_variant) = TYPE_POINTER_TO (fwdtype); TYPE_REFERENCE_TO (new_variant) = TYPE_REFERENCE_TO (fwdtype); } +#else + patch_type (type, otype); +#endif }
/* Declare the types */ diff -u p.nn/gpc.h p/gpc.h --- p.nn/gpc.h Sun Jul 3 02:30:53 2005 +++ p/gpc.h Wed Jul 6 17:33:22 2005 @@ -1182,6 +1182,7 @@ extern void pascal_expand_goto (tree); extern void do_setjmp (void); extern tree build_type_decl (tree, tree, tree); +extern void patch_type (tree type, tree otype); extern void declare_types (void); extern tree pascal_shadow_record_fields (tree, tree); extern void restore_identifiers (tree); diff -u p.nn/objects.c p/objects.c --- p.nn/objects.c Sun Jul 3 02:30:53 2005 +++ p/objects.c Wed Jul 6 17:58:17 2005 @@ -240,8 +240,23 @@ is_class = 1; if (is_class) { - res = build_pointer_type (t); - PASCAL_TYPE_CLASS (res) = 1; + tree s, *pscan; + pscan = ¤t_type_list; + for (s = current_type_list; s && TREE_VALUE (s) != name; + pscan = &TREE_CHAIN (s), s = TREE_CHAIN (s)) ; + if (s && TREE_CODE (TREE_TYPE (TREE_PURPOSE (s))) == POINTER_TYPE + && PASCAL_TYPE_CLASS (TREE_TYPE (TREE_PURPOSE (s))) + && TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_PURPOSE (s)))) == LANG_TYPE) + { + res = TREE_TYPE (TREE_PURPOSE (s)); + patch_type (t, TREE_TYPE (res)); + *pscan = TREE_CHAIN (s); + } + else + { + res = build_pointer_type (t); + PASCAL_TYPE_CLASS (res) = 1; + } } else res = t; diff -u p.nn/parse.y p/parse.y --- p.nn/parse.y Sun Jul 3 02:30:53 2005 +++ p/parse.y Wed Jul 6 14:27:55 2005 @@ -747,6 +747,12 @@ finish_object_type ($<ttype>4, $7, $8, $5 != NULL_TREE); yyerrok; } + | new_identifier enable_lce equals p_class LEX_RANGE p_end + { + tree t = build_pascal_pointer_type (make_node (LANG_TYPE)); + PASCAL_TYPE_CLASS (t) = 1; + build_type_decl ($1, t, NULL_TREE); + }
;
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.
Does it allow the same in nested scopes?
Yes.
procedure Test; type a = record b: c; end; c = ^Integer; begin end;
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.
Also, what happens if the name is a predefined type (or a name declared in outer scope)? Normal Pascal rules are that the new definition should be used (even if the definition follows use), are they obeyed?
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. Only explaining CW's solution to the problem, which does have a degree of elegant simplicity, even if it is not entirely standards compliant.
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).
ATM I have implemented OOP forward declaration:
type o = class .. 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, 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.
I would think it would be reasonable that it work only in reference-based-object mode both at the time of forward definition and the time of actual definition.
As described previously, forward definitions for non-pointer sized types is much harder, and forward definitions for non-reference based objects is much less commonly needed.
I'll try out your patch, hopefully today. I'll either bash it so it works with object .. end, or I'll try this:
type obj = class .. end; ... obj = object ... end;
which probably will work currently and solve my short term issue with object support.
Thanks, Peter.
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
At 12:32 +0200 7/7/05, Frank Heckenbach wrote:
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;
CW handles this as well. So it handles the two cases, an unknown identifier, and forward pointer references (as in, an unknown identifier after ^)
The handling of unknown identifiers is for the other cases (other than ^unknown).
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.
I'm not sure about BP, but I know CW often requires the @ before a procedure name for an assignment to a procedural variable to remote the ambiguity of whether to call it or not - and then introduces the new ambiguity of whether it is a procedural pointer or a pointer to a procedure pointer. Ie, in CW:
function doit( a: Integer ): Integer; begin return 1; end; type proc = function ( a: Integer ) : Integer; var a: proc; begin a := @doit; WriteLn( a( 10 ) ); // prints 1 end;
Perhaps BP does something equally weird.
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.
Any (or all) of the different syntax is fine by me.
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?
Agreed. Indeed, I would think it would be an error to forward declare (or define after a forward declare) any non-reference object.
Enjoy, Peter.
Peter N Lewis wrote:
function doit( a: Integer ): Integer; begin return 1; end; type proc = function ( a: Integer ) : Integer; var a: proc; begin a := @doit; WriteLn( a( 10 ) ); // prints 1 end;
Perhaps BP does something equally weird.
I think it does something differently weird. ;-)
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?
Agreed. Indeed, I would think it would be an error to forward declare (or define after a forward declare) any non-reference object.
Agreed. (Already the forward declaration should be an error.)
Frank
Waldek Hebisch wrote:
Peter N Lewis wrote:
[snip]
No, CW does not accept either syntax:
MyObject = object; nor MyObject = object .. end;
In case it helps, CodeWarrior Pascal (as well as MPW Pascal) also supports a forward directive declaration form for objects, i.e,:
MyObject = object; forward;
A test program using a slightly modified expample from CodeWarrior Pascal's documentation:
program ForwardObjectTest;
type objA = object;forward; objB = object {...} o : objA; { objB uses objA } {...} end;
{var dummy: integer;
type} objA = object {...} p : objB; { objB uses objA } {...} end; begin end.
The forward object type declaration must have a complete type declaration in the same type declaration part (like Pascal's requirement for pointer types). The commented out var declaration part and type keyword is what I used to test that requirement. Uncomment that part and you get:
Error : unresolved forward class reference to 'objA' ForwardObjectTest.p line 11 var
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.
type rec = record a: UndefinedA; b: UndefinedB; end; UndefinedA = ^Integer; UndefinedB = object end;
Note that UndefinedA = UInt32 is not legal, it must be a pointer or object.
It's actually not a bad methodology and makes it easy to write data structures that is self or mutually referential, but it does not add anything over pre defining pointers or object names as EP/Delphi.
Does it allow the same in nested scopes? AFAIU objects are not allowed in nested scopes, but pointers still make sense. Also, what happens if the name is a predefined type (or a name declared in outer scope)? Normal Pascal rules are that the new definition should be used (even if the definition follows use), are they obeyed?
CodeWarrior Pascal doesn't correctly handle nested scope forward pointer redeclarations and you need some additional erroneous code construct (such as a type mismatch) for the compiler to report an error. A snippet inserted into the above test program:
type UndefinedA = ^Integer;
procedure test;
type rec = record a: UndefinedA; end; UndefinedA = ^boolean;
var testrec : rec; begin new(testrec.a); testrec.a^ := true; end;
yields:
Error : type mismatch ForwardObjectTest.p line 36 testrec.a^ := true;
which indicates the compiler is incorrectly using the outer scope declaration for UndefinedA when compiling the inner scope rec type.
In fairness, I'll note CodeWarrior Pascal isn't the only Pascal compiler I've used that has problems in correctly handling nested scope use before redeclaration cases.
Gale Paeper gpaeper@empirenet.com
Gale Paeper wrote:
In case it helps, CodeWarrior Pascal (as well as MPW Pascal) also supports a forward directive declaration form for objects, i.e,:
MyObject = object; forward;
A test program using a slightly modified expample from CodeWarrior Pascal's documentation:
program ForwardObjectTest;
BTW, Waldek, are you collecting these test programs (also Peter's) and putting them into your patch (now or later), so we'll have them in place when integrating the patch? (As far as we decide to support the features, of course.)
The forward object type declaration must have a complete type declaration in the same type declaration part (like Pascal's requirement for pointer types). The commented out var declaration part and type keyword is what I used to test that requirement. Uncomment that part and you get:
Error : unresolved forward class reference to 'objA' ForwardObjectTest.p line 11 var
Good (in accordance with OOE, except for the syntax).
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.
type rec = record a: UndefinedA; b: UndefinedB; end; UndefinedA = ^Integer; UndefinedB = object end;
Note that UndefinedA = UInt32 is not legal, it must be a pointer or object.
It's actually not a bad methodology and makes it easy to write data structures that is self or mutually referential, but it does not add anything over pre defining pointers or object names as EP/Delphi.
Does it allow the same in nested scopes? AFAIU objects are not allowed in nested scopes, but pointers still make sense. Also, what happens if the name is a predefined type (or a name declared in outer scope)? Normal Pascal rules are that the new definition should be used (even if the definition follows use), are they obeyed?
CodeWarrior Pascal doesn't correctly handle nested scope forward pointer redeclarations and you need some additional erroneous code construct (such as a type mismatch) for the compiler to report an error. A snippet inserted into the above test program:
type UndefinedA = ^Integer;
procedure test;
type rec = record a: UndefinedA; end; UndefinedA = ^boolean;
var testrec : rec; begin new(testrec.a); testrec.a^ := true; end;
yields:
Error : type mismatch ForwardObjectTest.p line 36 testrec.a^ := true;
which indicates the compiler is incorrectly using the outer scope declaration for UndefinedA when compiling the inner scope rec type.
In fairness, I'll note CodeWarrior Pascal isn't the only Pascal compiler I've used that has problems in correctly handling nested scope use before redeclaration cases.
Indeed, it's a nasty thing to detect. I finally did in in GPC recently (az25.pas, fjf1059*.pas), but it was a bit of effort ...
Frank
Frank Heckenbach wrote:
Gale Paeper wrote:
[snip]
The forward object type declaration must have a complete type declaration in the same type declaration part (like Pascal's requirement for pointer types). The commented out var declaration part and type keyword is what I used to test that requirement. Uncomment that part and you get:
Error : unresolved forward class reference to 'objA' ForwardObjectTest.p line 11 var
Good (in accordance with OOE, except for the syntax).
The other part of OOE's deferred class requirement (i.e., "cannot be used in a class-inheritance-list before its complete definition is specified") is also enforced. A slight modification of the test program I've been using for an example:
program ForwardObjectTest1;
type objA = object; forward; objB = object {...} o : objA; { objB uses objA } {...} end;
{ WRONG - Can't inherit from a FORWARD declared object } { objC = object(objA)
end; } objA = object {...} p : objB; { objB uses objA } {...} end;
{ OK - Can inherit from a FORWARD object after it is fully defined } objD = object(objA) { objD inherits from objA } end;
begin end.
With the objC type declaration commented out the program compiles without error. Uncomment the objC type declaration and you get the follow compilation error with CodeWarrior Pascal:
Error : class 'objA' was declared 'FORWARD' or 'EXTERNAL' ForwardObjectTest1.p line 12 objC = object(objA)
For the sake of completeness and since the error message refers to external object declarations, I suppose CodeWarrior Pascal and MPW Pascal's support for external object declarations should be mentioned.
The basic purpose and reason for external object declaration is the same as it is for forward declared objects except it is used for mutually dependent object type declarations between unit interfaces.
Before an example program demonstrating the external object feature and how it can be used, a few notes on this particular MacPascal Object Pascal feature:
1. I'm not sure any real world program actually makes use of it.
2. Even Apple, who invented the feature for MPW Pascal, called code using external objects "ugly" and the code, even though it maybe useful, confusing.
3. It isn't the easiest feature to make use of. It takes some thought and care in organizing code to avoid circular unit interface dependency errors and unresolved external object declaration errors.
So, there's no misunderstanding, I'm not asking for GPC's support for MacPascal style objects to include external object declaration support. Put it in the "to be considered if someone really needs it" bin. I'm covering MacPascal external object declaration support so that Frank and Waldek are at least aware of the feature and have an example of its usage.
The example program and units are:
program ExternalObjectTest;
uses UObjA, UObjB; var ObjAInstance: ObjA; ObjBInstance: ObjB;
begin ObjAInstance := NewObjA; ObjBInstance := NewObjB; ObjAInstance.InitwithObjB(ObjBInstance); ObjBInstance.InitwithObjA(ObjAInstance); ObjAInstance.TestObjAsObjBReference; ObjBInstance.TestObjBsObjAReference; Dispose(ObjAInstance); Dispose(ObjBInstance); end.
unit UObjA;
interface
type ObjB = object; external; ObjA = object fObjB: ObjB; procedure InitwithObjB(InitObj: ObjB); procedure TestObjAsObjBReference; procedure TestIt; end;
function NewObjA: ObjA;
implementation
uses UObjB;
function NewObjA: ObjA; var obj: ObjA; begin new(obj); NewObjA := obj; end;
procedure ObjA.InitwithObjB(InitObj: ObjB); begin fObjB := InitObj; end;
procedure ObjA.TestObjAsObjBReference; begin writeln ('In ObjA calling fObjB'); fObjB.TestIt; end;
procedure ObjA.TestIt; begin writeln ('In ObjA TestIt'); end;
end.
unit UObjB;
interface
uses UObjA; type ObjA = object; external; ObjB = object fObjA: ObjA; procedure InitwithObjA(InitObj: ObjA); procedure TestObjBsObjAReference; procedure TestIt; end;
function NewObjB: ObjB;
implementation
function NewObjB: ObjB; var obj: ObjB; begin new(obj); NewObjB := obj; end;
procedure ObjB.InitwithObjA(InitObj: ObjA); begin fObjA := InitObj; end;
procedure ObjB.TestObjBsObjAReference; begin writeln ('In ObjB calling fObjA'); fObjA.TestIt; end;
procedure ObjB.TestIt; begin writeln ('In ObjB TestIt'); end;
end.
When compiled with CodeWarrior Pascal and subsequently ran, the program output is:
In ObjA calling fObjB In ObjB TestIt In ObjB calling fObjA In ObjA TestIt
Gale Paeper gpaeper@empirenet.com
Gale Paeper wrote:
The other part of OOE's deferred class requirement (i.e., "cannot be used in a class-inheritance-list before its complete definition is specified") is also enforced. A slight modification of the test program I've been using for an example:
OK, thanks.
For the sake of completeness and since the error message refers to external object declarations, I suppose CodeWarrior Pascal and MPW Pascal's support for external object declarations should be mentioned.
The basic purpose and reason for external object declaration is the same as it is for forward declared objects except it is used for mutually dependent object type declarations between unit interfaces.
Before an example program demonstrating the external object feature and how it can be used, a few notes on this particular MacPascal Object Pascal feature:
- I'm not sure any real world program actually makes use of it.
I'm glad to hear so. I guess I never understand why someone would want to do this. If two objects are mutually dependent, they obviously form one, not two, "units" in the normal meaning of the word. Well, perhaps it's a relic of 64KB times where size restrictions dictated such a split ...
- Even Apple, who invented the feature for MPW Pascal, called code
using external objects "ugly" and the code, even though it maybe useful, confusing.
:-)
- It isn't the easiest feature to make use of. It takes some thought
and care in organizing code to avoid circular unit interface dependency errors and unresolved external object declaration errors.
So, there's no misunderstanding, I'm not asking for GPC's support for MacPascal style objects to include external object declaration support. Put it in the "to be considered if someone really needs it" bin. I'm covering MacPascal external object declaration support so that Frank and Waldek are at least aware of the feature and have an example of its usage.
OK.
Frank