Comprobación de Tipos: Sentencia RETURN

La Forma Habitual

Veamos la forma del árbol en una sentencia de retorno:

pl@nereida:~/Lbook/code/Simple-Types/script$ perl -wd usetypes.pl prueba16.c 2
main::(usetypes.pl:5):  my $filename = shift || die "Usage:\n$0 file.c\n";
  DB<1> f Types.eyp
1       2       3       4       5       6       7       #line 8 "Types.eyp"
8
9:      use strict;
10:     use Carp;
  DB<2> c 598
1 int f() {
2   int a[30];
3
4   return a;
5 }
Simple::Types::compile(Types.eyp:598):
598:     set_types($t);        # Init basic types
  DB<3> insert_method(qw{PROGRAM FUNCTION}, 'footnote', sub {})
  DB<4> p $t->str
PROGRAM(FUNCTION[f](RETURN(VAR(TERMINAL[a:4]))))

Código de Comprobación de Tipos para Sentencias de Retorno

pl@nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple$ \
                               sed -ne '267,282p' Trans.trg | cat -n
     1  returntype: RETURN($ch)
     2    => {
     3        my $rt = $RETURN->{t};
     4
     5        $ch = char2int($RETURN, 0) if $rt == $INT;
     6        $ch = int2char($RETURN, 0) if $rt == $CHAR;
     7
     8          type_error("Type error in return statement", $ch->line)
     9        unless ($rt == $ch->{t});
    10
    11        # $RETURN->{t} has already the correct type
    12
    13        $RETURN->type(ref($RETURN).ref($rt));
    14
    15        return 1;

El análisis de ámbito de las sentencias de retorno se hizo de forma ligeramente distinta al resto. El siguiente fragmento de código figura en la subrutina compile justo despueés del resto del análisis de ámbito::

pl@nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple$ \
                               sed -ne '585,595p' Types.eyp | cat -n
     1   # Scope Analysis: Return-Function
     2   our $retscope; # retscope: /FUNCTION|RETURN/
     3   my @returns = $retscope->m($t);
     4   for (@returns) {
     5     my $node = $_->node;
     6     if (ref($node) eq 'RETURN') {
     7       my $function = $_->father->node;
     8       $node->{function}  = $function;
     9       $node->{t} = $function->{t}->child(1);
    10     }
    11   }

Ejemplos

pl@nereida:~/Lbook/code/Simple-Types/script$ usetypes.pl prueba16.c 2
1 int f() {
2   int a[30];
3
4   return a;
5 }
Type Error at line 4: Type error in return statement

El retorno vacío por parte de una función no se considera un error en C:

pl@nereida:~/Lbook/code/Simple-Types/script$ usetypes.pl prueba26.c 2 | head -12
1 int f() {
2   int a[30];
3
4   return;
5 }

PROGRAM^{0}(
  FUNCTION[f]^{1}(
    EMPTYRETURN
  )
) # PROGRAM
---------------------------

Véase la conducta de gcc ante el mismo programa:

pl@nereida:~/Lbook/code/Simple-Types/script$ gcc -c prueba26.c
pl@nereida:~/Lbook/code/Simple-Types/script$ ls -ltr | tail -1
-rw-r--r-- 1 pl users  690 2008-01-10 13:32 prueba26.o

Usando m

En esta sección seguiremos una aproximación diferente. En este caso queremos maximizar nuestro conocimiento

Para comprobar la correción del tipo del valor retornado vamos a usar el método m. La razón para hacerlo es familiarizarle con el uso de m.

Usaremos una transformación árbol bind_ret2function a la que llamaremos (línea 33) desde compile después de haber verificado el resto de la comprobación de tipos (línea 30). Podemos postponer este análisis ya que el padre de un nodo RETURN es un nodo FUNCTION y por tanto no hay nodos cuya comprobación de tipos dependa de la computación de tipo de un nodo RETURN.

nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple> \
    sed -ne '519,554p' Types.eyp | cat -n
 1  sub compile {
 2   my($self)=shift;
 3
..  ..........................................
29
30   $t->bud(@typecheck);
31
32   our $bind_ret2function;
33   my @FUNCTIONS = $bind_ret2function->m($t);
34
35   return $t;
36  }

bind_ret2function realiza la comprobación de tipos en los siguientes pasos:

  1. La expresión regular árbol casa con cualquier nodo de definición de función. Para cada uno de esos nodos se procede como sigue:
  2. Se buscan (línea 4) todas las sentencias RETURN en la función. Recuerde que Parse::Eyapp::Node::m retorna nodos Match y que los nodos del AST se obtienen usando el método node sobre el nodo Match (línea 5)
  3. Ponemos como atributo de la función las referencias a los nodos RETURN encontrados (línea 8)
  4. Para cada nodo de tipo RETURN encontrado:
    1. Ponemos como atributo function del nodo la función que le anida (línea 15)
    2. Se realizan las coherciones que fueran necesarias (líneas 17-19)
    3. Se emite un mensaje de error si el tipo de la expresión evaluada ($exp->{t}) no coincide con el declarado en la función ($return_type) (líneas 21-22)
    4. En la línea 25 cambiamos la clase del nodo de RETURN a RETURNINT o RETURNCHAR según sea el tipo retornado. Al igual que en las asignaciones eliminamos la sobrecarga semántica del nodo acercándonos a niveles mas bajos de programación.

Sigue el código. La línea 1 muestra una simple expresión regular árbol que casa con aquellas sentencias RETURN en las que se ha explicitado una expresión de retorno y que es usada por bind_ret2function.

nereida:~/doc/casiano/PLBOOK/PLBOOK/code/Simple-Types/lib/Simple> \
                              sed -ne '196,225p' Trans.trg | cat -n
 1  /* TIMTOWTDI when MOPping */
 2  return: RETURN(.)
 3  bind_ret2function: FUNCTION
 4    => {
 5      my @RETURNS = $return->m($FUNCTION);
 6      @RETURNS = map { $_->node } @RETURNS;
 7
 8      # Set "returns" attribute for the FUNCTION node
 9      $FUNCTION->{returns} = \@RETURNS;
10
11      my $exp;
12      my $return_type = $FUNCTION->{t}->child(1);
13      for (@RETURNS) {
14
15        # Set "function" attribute for each RETURN node
16        $_->{function} = $FUNCTION;
17
18        #always char-int conversion
19        $exp = char2int($_, 0) if $return_type == $INT;
20        $exp = int2char($_, 0) if $return_type == $CHAR;
21
22          type_error("Returned type does not match function declaration",
23                                                                $_->line)
24        unless $exp->{t} == $return_type;
25        $_->type("RETURN".ref($return_type));
26
27      }
28
29      return 1;
30    }

Obsérvese que la solución no optimiza la homogeneidad ni la generalidad ni el mantenimiento. Por ello, en condiciones normales de construcción de un compilador - como es el caso de la práctica 6.16 - es aconsejable abordar el análisis de tipos de la sentencia RETURN con la misma aproximación seguida para el resto de los nodos.



Subsecciones
Casiano Rodríguez León
2009-12-09