Page 1 of 1

Type Test Inconsistency?

Posted: Fri May 03, 2019 1:17 pm
by gray
From real-world code, distilled down to a test case:

Code: Select all

MODULE M;

  IMPORT Main, Out;
    
  TYPE
    T = RECORD i: INTEGER END;
    T1 = RECORD(T) k: INTEGER END;  
    R = RECORD t: T1 END;
    P = POINTER TO R;
    
  VAR
    p: P;
    
  PROCEDURE P2(VAR t: T);
  BEGIN
    t.i := 13;
    Out.String("T"); Out.Ln;
    CASE t OF T1:
      t.k := 4;
      Out.String("T1"); Out.Ln;
    END
  END P2;
    
  PROCEDURE P1(p: P);
  BEGIN
    P2(p.t)
  END P1;
    
BEGIN
  Out.String("reset"); Out.Ln;
  NEW(p);
  Out.String("case 1: P2 direct"); Out.Ln;
  P2(p.t);
  Out.String("case 2: P2 via P1"); Out.Ln;
  P1(p)
END M.
This prints:

Code: Select all

reset
case 1: P2 direct
T
T1
case 2: P2 via P1
T
My actual use case is "case 2", and I was baffled that the code within the CASE type test didn't execute. IMHO, both cases should yield the same result (case 1).

What do I miss?

PS: I just realised: still with the M3 7.0 compiler. Maybe 7.0.1 fixes that?

Re: Type Test Inconsistency?

Posted: Fri May 03, 2019 11:30 pm
by cfbsoftware
I suspect that this example is an attempt to circumvent the rule that does not allow type tests on expressions e.g. the following is invalid where p is a record:

Code: Select all

CASE p.t OF T1:
      t.k := 4;
      Out.String("T1"); Out.Ln;
    END
In this context p.t is an expression not a qualified identifier.

If you want to implement your example without any surprises stick to using pointers throughout:

Code: Select all

MODULE M;

  IMPORT Main, Out;
    
  TYPE
    T = RECORD i: INTEGER END;
    PT = POINTER TO T;

    T1 = RECORD(T) k: INTEGER END;  
    PT1 = POINTER TO T1;

    R = RECORD t: PT1 END;
    PR1 = POINTER TO R;
    
    
  VAR
    p: PR1;
    
  PROCEDURE P2(t: PT);
  BEGIN
    t.i := 13;
    Out.String("T"); Out.Ln;
    CASE t OF PT1:
      t.k := 4;
      Out.String("T1"); Out.Ln;
    END
  END P2;
    
  PROCEDURE P1(p: PR1);
  BEGIN
    P2(p.t)
  END P1;
    
BEGIN
  Out.String("reset"); Out.Ln;
  NEW(p);
  NEW(p.t);
  Out.String("case 1: P2 direct"); Out.Ln;
  P2(p.t);
  Out.String("case 2: P2 via P1"); Out.Ln;
  P1(p)
END M.

Re: Type Test Inconsistency?

Posted: Sat May 04, 2019 3:49 am
by gray
Assume P2 is implemented in another module, say implementing T and its extensions (my actual use case); the code is perfectly valid with a qualident as case variable. Now a module client calls P2 "wrongly", and P2 fails silently.

But the test-case should execute correctly with an IS type test.

Code: Select all

  PROCEDURE P2(VAR t: T);
  BEGIN
    t.i := 13;
    Out.String("T"); Out.Ln;
    IF t IS T1 THEN 
      t(T1).k := 4;
      Out.String("T1"); Out.Ln
    END
  END P2;
Same result (OMM, as said, M3 7.0 compiler).

Re: Type Test Inconsistency?

Posted: Sat May 04, 2019 5:23 am
by cfbsoftware
There is nothing wrong with procedure P2 in either example. I strongly recommend that you use pointers throughout to minimise confusion, but if you have a compelling reason to pass record elements around as in your example, and want to get your expected result, then you should make the pointer to PI a VAR parameter i.e.

Code: Select all

  PROCEDURE P1(VAR p: P);
  BEGIN
    P2(p.t)
  END P1;

Re: Type Test Inconsistency?

Posted: Sat May 11, 2019 3:45 am
by gray
Thanks, I have changed my code to use pointers now. As you say, it's clearer and cleaner.

Just out of interest, in the case without having the parameter for P1 declared as VAR, shouldn't the compiler flag the call to P2 from P1, as a read-only variable (p.t) is passed to a VAR parameter in P2?

Re: Type Test Inconsistency?

Posted: Sat May 11, 2019 4:58 am
by cfbsoftware
The value parameter that is passed to P1 is p, not p.t.

p is pointer. It is not a structured variable, so it is not read-only when it is passed as a value parameter. However, any changes to the value of p (not what it points to) only affect the local copy of the pointer.