[net.bugs.4bsd] C compiler bug etc. More fixes

donn@utah-cs.UUCP (08/29/84)

It seems that the fix I supplied uncovers another bug in the compiler.
Here is the result which Krzysztof Kozminski found -- a program that
used to work:

------------------------------------------------------------------------
main()                          
{       int big_number = ((~((unsigned) 0)) >> 1);
	printf("this is a big number: %d\n",big_number);
}                               
------------------------------------------------------------------------

... is suddenly broken -- it prints -1 instead of 2147483647.

What's going on is that the old compiler didn't evaluate the expression
'((~((unsigned) 0)) >> 1)' into a constant; instead it generated the
actual instructions to compute the result from the constants 0 and 1.
The new version is 'smarter' -- it realizes that it can compute the
expression ahead of time, but gets it wrong...  The fix is to force the
compiler to look for unsigned constants.  The following changes in
conval() in mip/trees.c will do the trick:

------------------------------------------------------------------------
*** /tmp/,RCSt1019513	Mon Aug 27 23:59:25 1984
--- trees.c	Mon Aug 27 23:34:41 1984
***************
*** 571,577
  		p->tn.lval -= val;
  		break;
  	case MUL:
! 		p->tn.lval *= val;
  		break;
  	case DIV:
  		if( val == 0 ) uerror( "division by 0" );

--- 571,578 -----
  		p->tn.lval -= val;
  		break;
  	case MUL:
! 		if ( u ) p->tn.lval = (unsigned) p->tn.lval * val;
! 		else p->tn.lval *= val;
  		break;
  	case DIV:
  		if( val == 0 ) uerror( "division by 0" );
***************
*** 575,580
  		break;
  	case DIV:
  		if( val == 0 ) uerror( "division by 0" );
  		else p->tn.lval /= val;
  		break;
  	case MOD:

--- 576,582 -----
  		break;
  	case DIV:
  		if( val == 0 ) uerror( "division by 0" );
+ 		else if ( u ) p->tn.lval = (unsigned) p->tn.lval / val;
  		else p->tn.lval /= val;
  		break;
  	case MOD:
***************
*** 579,584
  		break;
  	case MOD:
  		if( val == 0 ) uerror( "division by 0" );
  		else p->tn.lval %= val;
  		break;
  	case AND:

--- 581,587 -----
  		break;
  	case MOD:
  		if( val == 0 ) uerror( "division by 0" );
+ 		else if ( u ) p->tn.lval = (unsigned) p->tn.lval % val;
  		else p->tn.lval %= val;
  		break;
  	case AND:
***************
*** 596,602
  		break;
  	case RS:
  		i = val;
! 		p->tn.lval = p->tn.lval >> i;
  		break;
  
  	case UNARY MINUS:

--- 599,606 -----
  		break;
  	case RS:
  		i = val;
! 		if ( u ) p->tn.lval = (unsigned) p->tn.lval >> i;
! 		else p->tn.lval = p->tn.lval >> i;
  		break;
  
  	case UNARY MINUS:
------------------------------------------------------------------------

Tell me if I missed a case here...

Poking around in the code some more, I noticed another bug.  The
following C code:

------------------------------------------------------------------------
float x = (int) 2.3 / 3.2;
------------------------------------------------------------------------

... will elicit the following complaint from the compiler:

------------------------------------------------------------------------
x.c, line 1: compiler error: expression causes compiler loop: try simplifying
------------------------------------------------------------------------

This bug is again a matter of not simplifying constant expressions in
initializers.  The fix is in clocal() in pcc/local.c:

------------------------------------------------------------------------
*** /tmp/,RCSt1019533	Tue Aug 28 00:05:36 1984
--- local.c	Mon Aug 27 23:32:42 1984
***************
*** 85,91
  	case SCONV:
  		m = (p->in.type == FLOAT || p->in.type == DOUBLE );
  		ml = (p->in.left->in.type == FLOAT || p->in.left->in.type == DOUBLE );
! 		if( m != ml ) break;
  
  		/* now, look for conversions downwards */
  

--- 85,99 -----
  	case SCONV:
  		m = (p->in.type == FLOAT || p->in.type == DOUBLE );
  		ml = (p->in.left->in.type == FLOAT || p->in.left->in.type == DOUBLE );
! 		if ( ml && !m ) {
! 			/* float type to int type */
! 			r = block( ICON, NULL, NULL, INT, 0, 0 );
! 			r->tn.lval = (int) p->in.left->fpn.dval;
! 			r->tn.rval = NONAME;
! 			p->in.left->in.op = FREE;
! 			p->in.left = r;
! 			}
! 		else if ( ml != m  ) break;
  
  		/* now, look for conversions downwards */
  
------------------------------------------------------------------------

This probably doesn't catch everything...  I know of several other
ways to give your compiler fits:

------------------------------------------------------------------------
int i = (int) &i / 2;
------------------------------------------------------------------------

A similar but less horrible program also makes the compiler croak:

------------------------------------------------------------------------
int i = 1;
int j = 2;
int k = &j - &i;
------------------------------------------------------------------------

More fun and games:

------------------------------------------------------------------------
struct { int a, b, c; } x;
int i = &x.c - &x.i;
------------------------------------------------------------------------

In the same vein:

------------------------------------------------------------------------
char buf[50];
int i = &buf[40] - &buf[30];
------------------------------------------------------------------------

Unfortunately I don't know what to do about these...  Are they legal C?

Donn Seeley    University of Utah CS Dept    donn@utah-cs.arpa
40 46' 6"N 111 50' 34"W    (801) 581-5668    decvax!utah-cs!donn