SourceForge.net Logo

[Release b]
<t4m_bug_const_dflt>
Thu Jul 15 10:44:44 WEST 2004

In the current release of tendra4minix (a), after fixing <t4m_bug_usage>,
the program

int m;
void ini(const int n = 7) { m = n; }

produces this assembler output,

.sect .text; .sect .rom; .sect .data; .sect .bss
.sect .data
.extern _m__i
.comm _m__i, 4
.sect .text
.define _ini__Fi
.align 16
_ini__Fi:
mov (_m__i), 7
ret
.sect .text

and this TDF output,

TAG DECLARATIONS

m__i (variable) has access : - 
       .        and signature : - 
       .        and shape : integer(~signed_int*)

ini__Fi (identity) has access : - 
       .       .   and signature : - 
       .       .   and shape : proc


TAG DEFINITIONS

m__i (variable) is :
access : -
signature : -
make_int(~signed_int*,0)

ini__Fi (identity) is :
signature : -
make_proc(top,
       . |make_proc_arg(integer(~signed_int*),
       .       .       |-,
       .       .       |~tag_2),
       . |-,
       . |sequence(assign(obtain_tag(m__i),
       .       .       . |make_int(~signed_int*,7)),
       .       .  |return(make_top)))

(so it is a problem in the frontend).

This bug happens also with member functions, both virtual and non-virtual,
with member functions of class templates, exported or not, and even in
functions expanded inline (both the expansion and the non-expanded function
body suffer from this `constant propagation'). It also appears when declaring
function ini previously,

int m;
void ini(const int = 7);
int main() { ini(19); return 0; }
void ini(const int n) { m = n; }

Finally it happens even with pointers; this program

int *nptr;
void ini(int *const p = 0) { nptr = p; }

produces this assembler output,

.sect .text; .sect .rom; .sect .data; .sect .bss
.sect .data
.extern _nptr__Pi
.comm _nptr__Pi, 4
.sect .text
.define _ini__FPi
.align 16
_ini__FPi:
xor eax, eax
mov (_nptr__Pi), eax
ret
.sect .text

The problem does not show up with non-const parameters. It is not difficult
to imagine what's going on: the compiler is probably confusing the parameter
'n' with a const-declared variable. Here is a call-trace starting from the
function make_assign_exp [assign.c], called from the parser,

make_assign_exp, a = m :: exp_identifier_tag, b = n :: exp_identifier_tag,c = 0
  convert_reference, a = m :: exp_identifier_tag, context = 0
  convert_reference: returning d = m :: exp_identifier_tag
  convert_reference, a = n :: exp_identifier_tag, context = 2
  convert_reference: returning d = n :: exp_identifier_tag
  convert_assign, t = int :: type_integer_tag, a = n :: exp_identifier_tag
    cast_exp, t = int :: type_integer_tag, a = n :: exp_identifier_tag,cast = 0
      convert_lvalue, a = n :: exp_identifier_tag
        convert_const, a = n :: exp_identifier_tag
          /* Propagate simple constants */
        convert_const: returning d = 7 :: exp_int_lit_tag
      convert_lvalue: returning d = 7 :: exp_int_lit_tag
      cast_int_int, t = int :: type_integer_tag, a = 7, cast = 0, rank = -1
      cast_int_int: returning d = 7 :: exp_int_lit_tag
    cast_exp: returning d = 7 :: exp_int_lit_tag
  convert_assign: returning d = 7 :: exp_int_lit_tag
  make_preinc_exp, t = int, a = m :: exp_identifier_tag, b = 7, op = lex_assign
  make_preinc_exp: returning d = <exp> :: exp_assign_tag
make_assign_exp: returning d = <exp> :: exp_assign_tag

The problem lies in function convert_lvalue [convert.c]; it calls function
convert_const [convert.c] no matter what sort of identifier it is dealing with
(id_variable_tag, id_parameter_tag, etc.),

		/* Lvalue-to-rvalue conversion */
		ERROR err ;
		if ( qual == ( cv_const | cv_lvalue ) ) {
		    /* Check for constants at this stage */
		    if ( IS_exp_identifier ( a ) ) {
			e = convert_const ( a ) ;
			if ( !EQ_exp ( e, a ) ) return ( e ) ;
		    }
		}

Needless to say, the meaning of the keyword 'const' in function parameters is
not that of a "compilation constant", and its value is not necessarily the
default argument. To fix this bug, it will suffice to replace the lines above
with

		/* Lvalue-to-rvalue conversion */
		ERROR err ;
		if ( qual == ( cv_const | cv_lvalue ) ) {
		    /* Check for constants at this stage */
		    if ( IS_exp_identifier ( a ) ) {
			IDENTIFIER id = DEREF_id ( exp_identifier_id ( a ) ) ;
			/* Exclude const parameters and their default args. */
			if ( !IS_id_parameter ( id ) ) {
			    e = convert_const ( a ) ;
			    if ( !EQ_exp ( e, a ) ) return ( e ) ;
			}
		    }
		}