2.8 How to detect run time problems at compile time

Consider the following program:

MODULE assignvalue ;  (*!m2iso+gm2*)

PROCEDURE bad () : INTEGER ;
VAR
   i: INTEGER ;
BEGIN
   i := -1 ;
   RETURN i
END bad ;

VAR
   foo: CARDINAL ;
BEGIN
   (* The m2rte plugin will detect this as an error, post
      optimization.  *)
   foo := bad ()
END assignvalue.

here we see that the programmer has overlooked that the return value from ‘bad’ will cause an overflow to ‘foo’. If we compile the code with the following options:

$ gm2 -g -fsoft-check-all -O2 -c assignvalue.mod
assignvalue.mod:16:0:inevitable that this error will occur at run time,
assignment will result in an overflow

The gm2 semantic plugin is automatically run and will generate a warning message for every exception call which is known as reachable. It is highly advised to run the optimizer (‘-O2’ or ‘-O3’) with ‘-fsoft-check-all’ so that the compiler is able to run the optimizer and perform variable and flow analysis before the semantic plugin is invoked.

The ‘-Wuninit-variable-checking’ can be used to identify uninitialized variables within the first basic block in a procedure. The checking is limited to variables so long as they are not an array or set or a variant record or var parameter.

The following example detects whether a sub component within a record is uninitialized.

MODULE testlarge2 ;

TYPE
   color = RECORD
              r, g, b: CARDINAL ;
           END ;

   pixel = RECORD
              fg, bg: color ;
           END ;

PROCEDURE test ;
VAR
   p: pixel ;
BEGIN
   p.fg.r := 1 ;
   p.fg.g := 2 ;
   p.fg.g := 3 ;   (* Deliberate typo should be p.fg.b.  *)
   p.bg := p.fg ;  (* Accessing an uninitialized field.  *)
END test ;

BEGIN
   test
END testlarge2.
$ gm2 -c -Wuninit-variable-checking testlarge2.mod
testlarge2.mod:19:13: warning: In procedure ‘test’: attempting to
access expression before it has been initialized
   19 |    p.bg := p.fg ;  (* Accessing an uninitialized field.  *)
      |            ~^~~

The following example detects if an individual field is uninitialized.

MODULE testwithnoptr ;

TYPE
   Vec =  RECORD
             x, y: CARDINAL ;
          END ;

PROCEDURE test ;
VAR
   p: Vec ;
BEGIN
   WITH p DO
      x := 1 ;
      x := 2   (* Deliberate typo, user meant y.  *)
   END ;
   IF p.y = 2
   THEN
   END
END test ;

BEGIN
   test
END testwithnoptr.

The following example detects a record is uninitialized via a pointer variable in a ‘WITH’ block.

$ gm2 -g -c -Wuninit-variable-checking testwithnoptr.mod
testwithnoptr.mod:21:8: warning: In procedure ‘test’: attempting to
access expression before it has been initialized
   21 |    IF p.y = 2
      |       ~^~
MODULE testnew6 ;

FROM Storage IMPORT ALLOCATE ;

TYPE
   PtrToVec = POINTER TO RECORD
                            x, y: INTEGER ;
                         END ;

PROCEDURE test ;
VAR
   p: PtrToVec ;
BEGIN
   NEW (p) ;
   WITH p^ DO
      x := 1 ;
      x := 2   (* Deliberate typo, user meant y.  *)
   END ;
   IF p^.y = 2
   THEN
   END
END test ;


BEGIN
   test
END testnew6.
$ gm2 -g -c -Wuninit-variable-checking testnew6.mod
testnew6.mod:19:9: warning: In procedure ‘test’: attempting to
access expression before it has been initialized
   19 |    IF p^.y = 2
      |       ~~^~