Opened 7 weeks ago

#247 new defect

Wrong result type for sizeof(-)

Reported by: mlbrooks Owned by:
Priority: minor Component: cfa-cc
Version: 1.0 Keywords:
Cc:

Description

The result type of a sizeof(-) expression should be size_t, but it is currently long unsigned int. The difference is observable on 32-bit builds, including on a plain C example.

Our definition of size_t is taken from the host C installation's <stddef.h> and is, therefore, above reproach.

This definition has:

-m32
typedef unsigned int size_t;

-m64
typedef long unsigned int size_t;

C11 Reference Manual, pg 90 has:
"The value of the result of [sizeof] is implementation-defined, and its type (an unsigned integer type) is size_t, defined in <stddef.h> (and other headers)."

Present state has sizeof(-) returning type long unsigned int, always, which is correct for 64-bit and incorrect for 32-bit:

// CFA

// expect: collision always
// actual: collision 64-bit only
void f1( size_t ) {}
void f1( typeof( sizeof( 42 ) ) ) {}

// expect: collision 32-bit only
// actual: collision never
void f2( unsigned int ) {}
void f2( typeof( sizeof( 42 ) ) ) {}

// expect: collision 64-bit only
// actual: collision always
void f3( long unsigned int ) {}
void f3( typeof( sizeof( 42 ) ) ) {}

While it is true that the types unsigned int and long unsigned int are the same size on 32-bit, they are still different types:

// C

#include <stddef.h>

int main( int argc, char ** argv ) {
    unsigned int val = 0;
    long unsigned int lval = 0;

    unsigned int * p = & lval;
    long unsigned int * lp = &val;
}

GCC, actual and expected, 32- and 64-bit: initialization of ‘unsigned int *’ from incompatible pointer type ‘long unsigned int *’; initialization of ‘long unsigned int *’ from incompatible pointer type ‘unsigned int *’

Typing the sizeof expression in this way means CFA is unable to compile the following valid C program:

// C

#include <stddef.h>

int main( int argc, char ** argv ) {
    typeof( sizeof( 42 ) ) val = 0;
    size_t * p = & val;
}

GCC, 64-bit: Success
GCC, 32-bit: Success
CFA, 64-bit: Success
CFA, 32-bit: Fail

Typing the sizeof expression in this way means CFA incurs unintuitive conversion costs in sizeof-vs-size_t overloading scenarios, which leads to unexpected ambiguity:

// CFA

void foo( size_t ) {
    printf("foo, unsigned\n");
}

void foo( ptrdiff_t ) {
    printf("foo, signed\n");
}

int main(int argc, char** argv) {
    printf("size of long int: %ld\n", sizeof(long int));
    foo( sizeof(float) );       // sizeof => size_t
    foo( "hello" - "world" );   // difference of pointers => ptrdiff_t
}

CFA, 64-bit, actual and expected: Compiler success with program output:

size of long int: 8
foo, unsigned
foo, signed

CFA, 32-bit, actual: Compiler error. Ambiguity at foo(sizeof(float)), having resolved type unsigned long int, between the two declarations, both showing 1 unsafe conversion cost.

CFA, 32-bit, expected: Compiler success with program output:

size of long int: 4
foo, unsigned
foo, signed

Initial investigation into the feasibility of changing the sizeof type found:

  • SizeofExpr? gets its result type set by its default constructor, and so the "unsigned int" decision made at the conclusion of parsing. It is visible with -XCFA -p -XCFA -P -XCFA ast.
  • Validate/FindSpecialDecls?.h populates the size_t type into a global variable.
  • To fix, consider deferring the result-type decision from parse time until after Validate::findSpecialDecls, and using the type from the global variable.
  • There is no equivalent problem with ptrdiff_t being introduced by subtracting pointers, because that operator is given in the prelude, with the expected ptrdiff_t return type. The difficulty is introduced by the compiler-provided handling of sizeof, taken with the input-provided definition of size_t.

Change History (0)

Note: See TracTickets for help on using tickets.