Made C's sizeof operator work with initialized void variables.

Added regression tests that check cc65's handling of void variables.
This commit is contained in:
Greg King 2017-03-12 12:55:31 -04:00
parent a780df1fe1
commit 750a527100
7 changed files with 147 additions and 62 deletions

View file

@ -4,7 +4,7 @@
<title>cc65 Users Guide
<author><url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline>
<url url="mailto:gregdk@users.sf.net" name="Greg King">
<date>2016-06-11
<date>2017-02-27
<abstract>
cc65 is a C compiler for 6502 targets. It supports several 6502 based home
@ -688,14 +688,14 @@ This cc65 version has some extensions to the ISO C standard.
<p>
<item> cc65 allows the initialization of <tt/void/ variables. This may be
used to create variable structures that are more compatible with
used to create arbitrary structures that are more compatible with
interfaces written for assembler languages. Here is an example:
<tscreen><verb>
void GCmd = { (char)3, (unsigned)0x2000, (unsigned)0x3000 };
</verb></tscreen>
This will be translated as follows:
That will be translated as follows:
<tscreen><verb>
_GCmd:
@ -704,12 +704,19 @@ This cc65 version has some extensions to the ISO C standard.
.word $3000
</verb></tscreen>
Since the variable is of type <tt/void/ you may not use it as is.
Since the variable is of type <tt/void/, you may not use it as-is.
However, taking the address of the variable results in a <tt/void*/
which may be passed to any function expecting a pointer.
which may be passed to any function expecting a pointer. Also, the
<tt/sizeof/ operator will give the length of the initializer:
<tscreen><verb>
GLen = sizeof GCmd;
</verb></tscreen>
will assign the value 5 to <tt/GLen/.
See the <url url="geos.html" name="GEOS library document"> for examples
on how to use this feature.
on how to use that feature.
<p>
<item> cc65 implements flexible array struct members as defined in the C99 ISO

View file

@ -389,7 +389,10 @@ unsigned SizeOf (const Type* T)
switch (UnqualifiedType (T->C)) {
case T_VOID:
return 0; /* Assume voids have size zero */
/* A void variable is a cc65 extension.
** Get its size (in bytes).
*/
return T->A.U;
/* Beware: There's a chance that this triggers problems in other parts
of the compiler. The solution is to fix the callers, because calling
@ -438,7 +441,7 @@ unsigned SizeOf (const Type* T)
/* Array with unspecified size */
return 0;
} else {
return T->A.L * SizeOf (T + 1);
return T->A.U * SizeOf (T + 1);
}
default:

View file

@ -891,6 +891,7 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers)
case TOK_VOID:
NextToken ();
D->Type[0].C = T_VOID;
D->Type[0].A.U = 0;
D->Type[1].C = T_END;
break;
@ -2114,7 +2115,7 @@ NextMember:
static unsigned ParseVoidInit (void)
static unsigned ParseVoidInit (Type* T)
/* Parse an initialization of a void variable (special cc65 extension).
** Return the number of bytes initialized.
*/
@ -2181,6 +2182,9 @@ static unsigned ParseVoidInit (void)
/* Closing brace */
ConsumeRCurly ();
/* Number of bytes determined by initializer */
T->A.U = Size;
/* Return the number of bytes initialized */
return Size;
}
@ -2216,8 +2220,8 @@ static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers)
case T_VOID:
if (IS_Get (&Standard) == STD_CC65) {
/* Special cc65 extension in non ANSI mode */
return ParseVoidInit ();
/* Special cc65 extension in non-ANSI mode */
return ParseVoidInit (T);
}
/* FALLTHROUGH */

9
test/err/void-empty.c Normal file
View file

@ -0,0 +1,9 @@
/*
!!DESCRIPTION!! Uninitialized void variables
!!ORIGIN!! cc65 regression tests
!!LICENCE!! Public Domain
!!AUTHOR!! Greg King
*/
void test;
const void list;

11
test/err/void-size2.c Normal file
View file

@ -0,0 +1,11 @@
/*
!!DESCRIPTION!! Size of void cast
!!ORIGIN!! cc65 regression tests
!!LICENCE!! Public Domain
!!AUTHOR!! Greg King
*/
unsigned test (void)
{
return sizeof ((void)12345);
}

56
test/val/void-size1.c Normal file
View file

@ -0,0 +1,56 @@
/*
!!DESCRIPTION!! Getting the size of a void-type variable (cc65 extension)
!!ORIGIN!! cc65 regression tests
!!LICENCE!! Public Domain
!!AUTHOR!! Greg King
*/
static const void list1 = {
(char)1,
(char)2,
(char)3,
(char)4,
(char)5,
(char)6,
(char)7,
(char)8,
(char)9,
(char)0
};
static void list2 = {
1,
2,
3,
4,
5,
6,
7,
8,
9,
0
};
void list3 = {
(char)1,
(char)2,
(char)3,
(char)4,
&list1,
(char)6,
(char)7,
(char)8,
(char)9,
&list2
};
/* We know that the expression is constant; don't tell us. */
#pragma warn (const-comparison, off)
int main (void)
{
return sizeof list1 != 10
|| sizeof list2 != 20
|| sizeof list3 != 12;
}

View file

@ -1,15 +1,13 @@
/*
** testprogram for ANTIC instructions as defined in "_antic.h"
** test program for ANTIC instructions as defined in "_antic.h"
**
** 23-Feb-2017, Christian Krueger
*/
#include <conio.h>
#include <atari.h>
#include <peekpoke.h>
#include <string.h>
// code is only for testing purposes, as screen and display list are not aligned
// code is only for testing purposes, as screen and display list are not aligned,
// and jumps not set!
unsigned char DummyScreen[400];
@ -43,17 +41,15 @@ void DisplayList = {
DL_JMP
};
unsigned char dlend = 0;
/* We know that the sizeof expression is constant; don't tell us. */
#pragma warn (const-comparison, off)
int
main(void)
{
// unfortunately "sizeof()" doesn't work with void data
// (Error: Size of data type is unknown)
// so we trick with the addresses at front and end...
int returnValue = (((unsigned int)&dlend-(unsigned int)&DisplayList) != 28); // assure only one byte per instruction!
int returnValue = (sizeof DisplayList != 28); // assure only one byte per instruction!
clrscr();
if (returnValue)
@ -66,4 +62,3 @@ main(void)
return returnValue;
}