Page 1 of 1

Extended Type Formal VAR Parameters

Posted: Sat Apr 13, 2019 4:17 am
by gray

Code: Select all

MODULE M;

  TYPE
    T1 = POINTER TO T1Desc;
    T1Desc = RECORD
    END;
    
    T2 = POINTER TO T2Desc;
    T2Desc = RECORD(T1Desc)
    END;
    
  VAR
    t1: T1;
    t2: T2;
    
  PROCEDURE P1(t: T1);
  END P1;
  
  PROCEDURE P2(VAR t: T1);
  END P2;
  
BEGIN
  t1 := t2;
  P1(t2);
  P2(t2) (* does not compile *)
END M.
I am not sure why the compiler rejects the assignment to the VAR procedure parameter in P2.

Re: Extended Type Formal VAR Parameters

Posted: Sat Apr 13, 2019 5:02 am
by cfbsoftware
Refer to section 10.1. Formal parameters in the Oberon Language Report. In particular:
The type of each formal parameter is specified in the parameter list. For variable parameters, it must be identical to the corresponding actual parameter's type, except in the case of a record, where it must be a base type of the corresponding actual parameter's type.
At compile time, the type of the actual parameter in your call to P2 is T2 whereas it should be T1. The call to P1 is OK because the formal parameter to P1 is not a variable parameter.

The following example is OK:

Code: Select all

MODULE M2;

  TYPE
    T1 = POINTER TO T1Desc;
    T1Desc = RECORD
    END;
    
    T2 = POINTER TO T2Desc;
    T2Desc = RECORD(T1Desc)
    END;
    
  VAR
    t1: T1;
    t2: T2;
    t1d: T1Desc;
    t2d: T2Desc;
    
  PROCEDURE P1(t: T1);
  END P1;
  
  PROCEDURE P2(VAR t: T1);
  END P2;
  
  PROCEDURE P1d(VAR t: T1Desc);
  END P1d;
  
BEGIN
  t1 := t2;
  t1d := t2d;
  P1(t2);
  P2(t1);
  P1d(t2d) 
END M2.

Re: Extended Type Formal VAR Parameters

Posted: Sun Apr 14, 2019 1:30 am
by gray
Hm, thanks, yes, the Report is clear about this. Mr W's writing is succinct and precise as usual. I was mulling over the reason why pointers to an extended type cannot be passed as VAR parameters declared as pointer to a base type thereof.

Is this the (or a) use case and problem the rule prevents?

Code: Select all

MODULE M9;

  TYPE
    T1 = POINTER TO T1Desc;
    T1Desc = RECORD
      i: INTEGER
    END;
    
    T2 = POINTER TO T2Desc;
    T2Desc = RECORD(T1Desc)
      k: INTEGER
    END;
  
  VAR
    t2: T2;
    
  PROCEDURE P1(VAR t: T1);
    VAR
      t1: T1;
  BEGIN
    NEW(t1);
    t := t1
  END P1;
  
BEGIN
  NEW(t2);
  t2.i := 4;
  t2.k := 13;
  P1(t2); (* error *)
  t2.k := 17 (* ouch *)
END M9.

Re: Extended Type Formal VAR Parameters

Posted: Sun Apr 14, 2019 4:16 am
by cfbsoftware
I can recommend an excellent reference to help you to understand the reasoning behind these sorts of details. It is the book titled Object-Oriented Programming in Oberon-2 by Hanspeter Mössenböck. It is available as PDF with the friendly permission of Springer-Verlag from :

http://ssw.jku.at/Research/Books/Oberon2.pdf

Keep in mind while you are reading it that Oberon-2 has additional O-O features (e.g. type-bound procedures), and some differences (e.g. WITH instead of CASE) so it is not all relevant to Oberon-07. However, there should still be enough to be useful to you.

Re: Extended Type Formal VAR Parameters

Posted: Mon Apr 15, 2019 2:30 am
by gray

Code: Select all

MODULE M8;

  TYPE
    T1 = RECORD
      i: INTEGER
    END;
    
    T2 = RECORD(T1)
      k: INTEGER
    END;
  
  VAR
    t1: T1;
    t2: T2;
    
  PROCEDURE P1(VAR t: T1);
    VAR
      t1: T1;
  BEGIN
    t1.i := 17;
    t := t1
  END P1;
  
BEGIN
  t2 := t1; (* illegal *)
  t2.i := 13;
  t2.k := 4;
  P1(t2); (* t2.i = 17, t2.k = 4 *)
END M8.
The t2 := t1 assignment is illegal, as per compiler error as well as the Report. However, P1(t2) and the assignment therein works and yields the results as indicated in the comment. Is this legal code as per the language definition? If yes, what are the rules of the assignment in P1 as regards the record fields in T2 but not in T1?

Re: Extended Type Formal VAR Parameters

Posted: Mon Apr 15, 2019 8:48 am
by cfbsoftware
That all looks good to me. In this case the assignment:

Code: Select all

t := t1
is equivalent to:

Code: Select all

t.i := t1.i
t2.k remains unchanged. If you wanted to modify k as well you would need to use an assignment with a type guard (and a type test to be on the safe side) within the procedure P1.

Code: Select all

PROCEDURE P1(VAR t: T1);
    VAR
      t1: T1;
  BEGIN
    t1.i := 17;
    t := t1;
    IF t IS T2 THEN t(T2).k := 99 END;
  END P1;
Alternatively, if you prefer, you can use CASE to handle both the type test and guard:

Code: Select all

  PROCEDURE P1(VAR t: T1);
    VAR
      t1: T1;
  BEGIN
    t1.i := 17;
    t := t1;
    CASE t OF T2: t.k := 99 END;
  END P1;