mirror of
https://github.com/cc65/cc65.git
synced 2024-06-08 15:29:37 +00:00
Merge branch 'master' into vsprint
This commit is contained in:
commit
9a1e0bac72
2
.github/checks/spaces.sh
vendored
2
.github/checks/spaces.sh
vendored
|
@ -5,7 +5,7 @@ CHECK_PATH=.
|
|||
|
||||
cd $SCRIPT_PATH/../../
|
||||
|
||||
FILES=`find $CHECK_PATH -type f \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l ' $'`
|
||||
FILES=`find $CHECK_PATH -type f \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "test/" | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l ' $'`
|
||||
|
||||
cd $OLDCWD
|
||||
|
||||
|
|
2
.github/checks/tabs.sh
vendored
2
.github/checks/tabs.sh
vendored
|
@ -5,7 +5,7 @@ CHECK_PATH=.
|
|||
|
||||
cd $SCRIPT_PATH/../../
|
||||
|
||||
FILES=`find $CHECK_PATH -type f \( \( -name \*.inc -a \! -name Makefile.inc \) -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l $'\t'`
|
||||
FILES=`find $CHECK_PATH -type f \( \( -name \*.inc -a \! -name Makefile.inc \) -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "test/" | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l $'\t'`
|
||||
|
||||
cd $OLDCWD
|
||||
|
||||
|
|
|
@ -3697,13 +3697,7 @@ void g_dec (unsigned flags, unsigned long val)
|
|||
} else {
|
||||
/* Inline the code */
|
||||
if (val < 0x300) {
|
||||
if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && val == 1) {
|
||||
unsigned L = GetLocalLabel();
|
||||
AddCodeLine ("bne %s", LocalLabelName (L));
|
||||
AddCodeLine ("dex");
|
||||
g_defcodelabel (L);
|
||||
AddCodeLine ("dea");
|
||||
} else if ((val & 0xFF) != 0) {
|
||||
if ((val & 0xFF) != 0) {
|
||||
unsigned L = GetLocalLabel();
|
||||
AddCodeLine ("sec");
|
||||
AddCodeLine ("sbc #$%02X", (unsigned char) val);
|
||||
|
|
|
@ -79,7 +79,6 @@
|
|||
static void Parse (void)
|
||||
/* Top level parser routine. */
|
||||
{
|
||||
int comma;
|
||||
SymEntry* Sym;
|
||||
FuncDesc* FuncDef = 0;
|
||||
|
||||
|
@ -89,16 +88,18 @@ static void Parse (void)
|
|||
/* Fill up the next token with a bogus semicolon and start the tokenizer */
|
||||
NextTok.Tok = TOK_SEMI;
|
||||
NextToken ();
|
||||
NextToken ();
|
||||
|
||||
/* Parse until end of input */
|
||||
while (CurTok.Tok != TOK_CEOF) {
|
||||
|
||||
DeclSpec Spec;
|
||||
int Comma;
|
||||
int NeedClean = 0;
|
||||
unsigned PrevErrorCount = ErrorCount;
|
||||
|
||||
/* Check for empty statements */
|
||||
if (CurTok.Tok == TOK_SEMI) {
|
||||
/* TODO: warn on this if we have a pedantic mode */
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
|
@ -119,7 +120,7 @@ static void Parse (void)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Read variable defs and functions */
|
||||
/* Read the declaration specifier */
|
||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_EXTERN | SC_STATIC);
|
||||
|
||||
/* Don't accept illegal storage classes */
|
||||
|
@ -138,18 +139,30 @@ static void Parse (void)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* If we haven't got a type specifier yet, something must be wrong */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* Avoid extra errors if it was a failed type specifier */
|
||||
if ((Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Error ("Declaration specifier expected");
|
||||
}
|
||||
NeedClean = -1;
|
||||
goto EndOfDecl;
|
||||
}
|
||||
|
||||
/* Read declarations for this type */
|
||||
Sym = 0;
|
||||
comma = 0;
|
||||
Comma = 0;
|
||||
while (1) {
|
||||
|
||||
Declarator Decl;
|
||||
|
||||
Sym = 0;
|
||||
|
||||
/* Read the next declaration */
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_NEED_IDENT);
|
||||
if (Decl.Ident[0] == '\0') {
|
||||
Sym = 0;
|
||||
goto NextDecl;
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Bail out if there are errors */
|
||||
if (NeedClean <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if we must reserve storage for the variable. We do this,
|
||||
|
@ -191,10 +204,6 @@ static void Parse (void)
|
|||
FuncDef->Flags = (FuncDef->Flags & ~FD_EMPTY) | FD_VOID_PARAM;
|
||||
}
|
||||
} else {
|
||||
if (CurTok.Tok != TOK_COMMA && CurTok.Tok != TOK_SEMI) {
|
||||
Error ("Expected ',' or ';' after top level declarator");
|
||||
}
|
||||
|
||||
/* Just a declaration */
|
||||
Decl.StorageClass |= SC_DECL;
|
||||
}
|
||||
|
@ -317,50 +326,50 @@ static void Parse (void)
|
|||
|
||||
}
|
||||
|
||||
NextDecl:
|
||||
/* Check for end of declaration list */
|
||||
if (CurTok.Tok == TOK_COMMA) {
|
||||
NextToken ();
|
||||
comma = 1;
|
||||
} else {
|
||||
if (CurTok.Tok != TOK_COMMA) {
|
||||
break;
|
||||
}
|
||||
Comma = 1;
|
||||
Spec.Flags |= DS_NO_EMPTY_DECL;
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
/* Finish the declaration */
|
||||
if (Sym) {
|
||||
/* Function definition? */
|
||||
if (IsTypeFunc (Sym->Type) && CurTok.Tok == TOK_LCURLY) {
|
||||
if (IsTypeFunc (Spec.Type) && TypeCmp (Sym->Type, Spec.Type).C >= TC_EQUAL) {
|
||||
/* ISO C: The type category in a function definition cannot be
|
||||
** inherited from a typedef.
|
||||
*/
|
||||
Error ("Function cannot be defined with a typedef");
|
||||
} else if (comma) {
|
||||
/* ISO C: A function definition cannot shall its return type
|
||||
** specifier with other declarators.
|
||||
*/
|
||||
Error ("';' expected after top level declarator");
|
||||
}
|
||||
if (Sym && IsTypeFunc (Sym->Type) && CurTok.Tok == TOK_LCURLY) {
|
||||
/* A function definition is not terminated with a semicolon */
|
||||
if (IsTypeFunc (Spec.Type) && TypeCmp (Sym->Type, Spec.Type).C >= TC_EQUAL) {
|
||||
/* ISO C: The type category in a function definition cannot be
|
||||
** inherited from a typedef.
|
||||
*/
|
||||
Error ("Function cannot be defined with a typedef");
|
||||
} else if (Comma) {
|
||||
/* ISO C: A function definition cannot shall its return type
|
||||
** specifier with other declarators.
|
||||
*/
|
||||
Error ("';' expected after top level declarator");
|
||||
}
|
||||
|
||||
/* Parse the function body anyways */
|
||||
NeedClean = 0;
|
||||
NewFunc (Sym, FuncDef);
|
||||
/* Parse the function body anyways */
|
||||
NeedClean = 0;
|
||||
NewFunc (Sym, FuncDef);
|
||||
|
||||
/* Make sure we aren't omitting any work */
|
||||
CheckDeferredOpAllDone ();
|
||||
/* Make sure we aren't omitting any work */
|
||||
CheckDeferredOpAllDone ();
|
||||
} else if (NeedClean > 0) {
|
||||
/* Must be followed by a semicolon */
|
||||
if (CurTok.Tok != TOK_SEMI) {
|
||||
Error ("',' or ';' expected after top level declarator");
|
||||
NeedClean = -1;
|
||||
} else {
|
||||
/* Must be followed by a semicolon */
|
||||
if (ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
}
|
||||
NextToken ();
|
||||
NeedClean = 0;
|
||||
}
|
||||
}
|
||||
|
||||
EndOfDecl:
|
||||
/* Try some smart error recovery */
|
||||
if (PrevErrorCount != ErrorCount && NeedClean < 0) {
|
||||
if (NeedClean < 0) {
|
||||
SmartErrorSkip (1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,24 +49,30 @@
|
|||
/* Remove unused loads and stores */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
unsigned OptLongAssign (CodeSeg* S)
|
||||
/* Simplify long assignments.
|
||||
** Recognize
|
||||
** lda ... 0
|
||||
** lda #IMM 0
|
||||
** sta sreg+1 1
|
||||
** lda ... 2
|
||||
** lda #IMM 2
|
||||
** sta sreg 3
|
||||
** lda ... 4
|
||||
** ldx ... 5
|
||||
** sta M0002 6
|
||||
** stx M0002+1 7
|
||||
** lda #IMM 4
|
||||
** ldx #IMM 5
|
||||
** sta YYY 6
|
||||
** stx YYY+1 7
|
||||
** ldy sreg 8
|
||||
** sty M0002+2 9
|
||||
** sty YYY+2 9
|
||||
** ldy sreg+1 10
|
||||
** sty M0002+3 11
|
||||
** and simplify if not used right after.
|
||||
** sty YYY+3 11
|
||||
** and simplify, if not used right after and no branching occurs, to
|
||||
** lda XXX+3
|
||||
** sta YYY+3
|
||||
** lda XXX+2
|
||||
** sta YYY+2
|
||||
** ldx XXX
|
||||
** lda XXX+1
|
||||
** sta YYY
|
||||
** stx YYY+1
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
|
@ -75,35 +81,43 @@ unsigned OptLongAssign (CodeSeg* S)
|
|||
unsigned I = 0;
|
||||
while (I < CS_GetEntryCount (S)) {
|
||||
|
||||
CodeEntry* L[12];
|
||||
CodeEntry* L[13];
|
||||
|
||||
/* Get next entry */
|
||||
L[0] = CS_GetEntry (S, I);
|
||||
|
||||
if (CS_GetEntries (S, L+1, I+1, 11)) {
|
||||
if (L[0]->OPC == OP65_LDA &&
|
||||
if (CS_GetEntries (S, L+1, I+1, 12)) {
|
||||
if (/* Check the opcode sequence */
|
||||
L[0]->OPC == OP65_LDA &&
|
||||
L[1]->OPC == OP65_STA &&
|
||||
!strcmp (L[1]->Arg, "sreg+1") &&
|
||||
L[2]->OPC == OP65_LDA &&
|
||||
L[3]->OPC == OP65_STA &&
|
||||
!strcmp (L[3]->Arg, "sreg") &&
|
||||
L[4]->OPC == OP65_LDA &&
|
||||
L[5]->OPC == OP65_LDX &&
|
||||
L[6]->OPC == OP65_STA &&
|
||||
L[7]->OPC == OP65_STX &&
|
||||
!strncmp(L[7]->Arg, L[6]->Arg, strlen(L[6]->Arg)) &&
|
||||
!strcmp(L[7]->Arg + strlen(L[6]->Arg), "+1") &&
|
||||
L[8]->OPC == OP65_LDY &&
|
||||
!strcmp (L[8]->Arg, "sreg") &&
|
||||
L[9]->OPC == OP65_STY &&
|
||||
!strncmp(L[9]->Arg, L[6]->Arg, strlen(L[6]->Arg)) &&
|
||||
!strcmp(L[9]->Arg + strlen(L[6]->Arg), "+2") &&
|
||||
L[10]->OPC == OP65_LDY &&
|
||||
!strcmp (L[10]->Arg, "sreg+1") &&
|
||||
L[11]->OPC == OP65_STY &&
|
||||
!strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) &&
|
||||
/* Check the arguments match */
|
||||
L[0]->AM == AM65_IMM &&
|
||||
!strcmp (L[1]->Arg, "sreg+1") &&
|
||||
L[2]->AM == AM65_IMM &&
|
||||
!strcmp (L[3]->Arg, "sreg") &&
|
||||
L[4]->AM == AM65_IMM &&
|
||||
L[5]->AM == AM65_IMM &&
|
||||
!strncmp(L[7]->Arg, L[6]->Arg, strlen(L[6]->Arg)) &&
|
||||
!strcmp(L[7]->Arg + strlen(L[6]->Arg), "+1") &&
|
||||
!strcmp (L[8]->Arg, "sreg") &&
|
||||
!strncmp(L[9]->Arg, L[6]->Arg, strlen(L[6]->Arg)) &&
|
||||
!strcmp(L[9]->Arg + strlen(L[6]->Arg), "+2") &&
|
||||
!strcmp (L[10]->Arg, "sreg+1") &&
|
||||
!strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) &&
|
||||
!strcmp(L[11]->Arg + strlen(L[6]->Arg), "+3") &&
|
||||
!RegXUsed (S, I+11)) {
|
||||
/* Check there's nothing more */
|
||||
!RegXUsed (S, I+12) &&
|
||||
!CS_RangeHasLabel(S, I, 12)) {
|
||||
|
||||
L[1]->AM = L[11]->AM;
|
||||
CE_SetArg(L[1], L[11]->Arg);
|
||||
|
@ -142,7 +156,15 @@ unsigned OptLongCopy (CodeSeg* S)
|
|||
** sty YYY+2 9
|
||||
** ldy sreg+1 10
|
||||
** sty YYY+3 11
|
||||
** and simplify if not used right after.
|
||||
** and simplify, if not used right after and no branching occurs, to
|
||||
** lda XXX+3
|
||||
** sta YYY+3
|
||||
** lda XXX+2
|
||||
** sta YYY+2
|
||||
** ldx XXX
|
||||
** lda XXX+1
|
||||
** sta YYY
|
||||
** stx YYY+1
|
||||
*/
|
||||
{
|
||||
unsigned Changes = 0;
|
||||
|
@ -151,12 +173,12 @@ unsigned OptLongCopy (CodeSeg* S)
|
|||
unsigned I = 0;
|
||||
while (I < CS_GetEntryCount (S)) {
|
||||
|
||||
CodeEntry* L[12];
|
||||
CodeEntry* L[13];
|
||||
|
||||
/* Get next entry */
|
||||
L[0] = CS_GetEntry (S, I);
|
||||
|
||||
if (CS_GetEntries (S, L+1, I+1, 11)) {
|
||||
if (CS_GetEntries (S, L+1, I+1, 12)) {
|
||||
if (L[0]->OPC == OP65_LDA &&
|
||||
!strncmp(L[0]->Arg, L[5]->Arg, strlen(L[5]->Arg)) &&
|
||||
!strcmp(L[0]->Arg + strlen(L[5]->Arg), "+3") &&
|
||||
|
@ -185,7 +207,8 @@ unsigned OptLongCopy (CodeSeg* S)
|
|||
L[11]->OPC == OP65_STY &&
|
||||
!strncmp(L[11]->Arg, L[6]->Arg, strlen(L[6]->Arg)) &&
|
||||
!strcmp(L[11]->Arg + strlen(L[6]->Arg), "+3") &&
|
||||
!RegXUsed (S, I+11)) {
|
||||
!RegXUsed (S, I+11) &&
|
||||
!CS_RangeHasLabel(S, I, 12)) {
|
||||
|
||||
L[1]->AM = L[11]->AM;
|
||||
CE_SetArg(L[1], L[11]->Arg);
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
|
||||
|
||||
static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSpecified);
|
||||
static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags);
|
||||
/* Parse a type specifier */
|
||||
|
||||
|
||||
|
@ -83,125 +83,6 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
|
||||
|
||||
|
||||
static void OpenBrace (Collection* C, token_t Tok)
|
||||
/* Consume an opening parenthesis/bracket/curly brace and remember that */
|
||||
{
|
||||
switch (Tok) {
|
||||
case TOK_LPAREN: Tok = TOK_RPAREN; break;
|
||||
case TOK_LBRACK: Tok = TOK_RBRACK; break;
|
||||
case TOK_LCURLY: Tok = TOK_RCURLY; break;
|
||||
default: Internal ("Unexpected opening token: %02X", (unsigned)Tok);
|
||||
}
|
||||
CollAppend (C, (void*)Tok);
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CloseBrace (Collection* C, token_t Tok)
|
||||
/* Consume a closing parenthesis/bracket/curly brace if it is matched with an
|
||||
** opening one and return 0, or bail out and return -1 if it is not matched.
|
||||
*/
|
||||
{
|
||||
if (CollCount (C) > 0) {
|
||||
token_t LastTok = (token_t)CollLast (C);
|
||||
if (LastTok == Tok) {
|
||||
CollPop (C);
|
||||
NextToken ();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int SmartErrorSkip (int WholeDecl)
|
||||
/* Try some smart error recovery.
|
||||
**
|
||||
** - If WholeDecl is 0:
|
||||
** Skip tokens until a comma or closing curly brace that is not enclosed in
|
||||
** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or
|
||||
** unpaired right parenthesis/bracket/curly brace is reached.
|
||||
**
|
||||
** - If WholeDecl is non-0:
|
||||
** Skip tokens until a closing curly brace that is not enclosed in an open
|
||||
** parenthesis/bracket/curly brace, or until a semicolon or EOF is reached.
|
||||
**
|
||||
** Return 0 if this exits as soon as it reaches an EOF. Return 0 as well if
|
||||
** this exits with no open parentheses/brackets/curly braces. Otherwise, return
|
||||
** -1.
|
||||
*/
|
||||
{
|
||||
Collection C = AUTO_COLLECTION_INITIALIZER;
|
||||
int Res = 0;
|
||||
|
||||
/* Some fix point tokens that are used for error recovery */
|
||||
static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI,
|
||||
TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY };
|
||||
|
||||
while (CurTok.Tok != TOK_CEOF) {
|
||||
SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0]));
|
||||
|
||||
switch (CurTok.Tok) {
|
||||
case TOK_LPAREN:
|
||||
case TOK_LBRACK:
|
||||
case TOK_LCURLY:
|
||||
OpenBrace (&C, CurTok.Tok);
|
||||
break;
|
||||
|
||||
case TOK_RPAREN:
|
||||
case TOK_RBRACK:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
if (!WholeDecl) {
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_RCURLY:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
if (!WholeDecl) {
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
} else if (CollCount (&C) == 0) {
|
||||
goto ExitPoint;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_COMMA:
|
||||
if (CollCount (&C) == 0 && !WholeDecl) {
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
break;
|
||||
|
||||
case TOK_SEMI:
|
||||
if (CollCount (&C) != 0) {
|
||||
Res = -1;
|
||||
}
|
||||
goto ExitPoint;
|
||||
|
||||
case TOK_CEOF:
|
||||
goto ExitPoint;
|
||||
|
||||
default:
|
||||
Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok);
|
||||
}
|
||||
}
|
||||
|
||||
ExitPoint:
|
||||
DoneCollection (&C);
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static unsigned ParseOneStorageClass (void)
|
||||
/* Parse and return a storage class specifier */
|
||||
{
|
||||
|
@ -451,20 +332,36 @@ static void OptionalInt (void)
|
|||
|
||||
|
||||
|
||||
static void OptionalSigned (int* SignednessSpecified)
|
||||
static void OptionalSigned (DeclSpec* Spec)
|
||||
/* Eat an optional "signed" token */
|
||||
{
|
||||
if (CurTok.Tok == TOK_SIGNED) {
|
||||
/* Skip it */
|
||||
NextToken ();
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
if (Spec != NULL) {
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void UseDefaultType (DeclSpec* Spec, typespec_t TSFlags)
|
||||
/* Use the default type for the type specifier */
|
||||
{
|
||||
if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) {
|
||||
Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_NONE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
} else {
|
||||
Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_DEF_TYPE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void InitDeclSpec (DeclSpec* Spec)
|
||||
/* Initialize the DeclSpec struct for use */
|
||||
{
|
||||
|
@ -1080,9 +977,15 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
|||
|
||||
/* Get the type of the entry */
|
||||
DeclSpec Spec;
|
||||
int SignednessSpecified = 0;
|
||||
int NeedClean = 0;
|
||||
|
||||
/* Check for extra semicolons */
|
||||
if (CurTok.Tok == TOK_SEMI) {
|
||||
/* TODO: warn on this if we have a pedantic mode */
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for a _Static_assert */
|
||||
if (CurTok.Tok == TOK_STATIC_ASSERT) {
|
||||
ParseStaticAssert ();
|
||||
|
@ -1090,7 +993,27 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
|||
}
|
||||
|
||||
InitDeclSpec (&Spec);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
||||
|
||||
/* Check if this is only a type declaration */
|
||||
if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
CheckEmptyDecl (&Spec);
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we haven't got a type specifier yet, something must be wrong */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* Avoid extra errors if it was a failed type specifier */
|
||||
if ((Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Error ("Declaration specifier expected");
|
||||
}
|
||||
NeedClean = -1;
|
||||
goto EndOfDecl;
|
||||
}
|
||||
|
||||
/* Allow anonymous bit-fields */
|
||||
Spec.Flags |= DS_ALLOW_BITFIELD;
|
||||
|
||||
/* Read fields with this type */
|
||||
while (1) {
|
||||
|
@ -1098,7 +1021,12 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
|||
Declarator Decl;
|
||||
|
||||
/* Get type and name of the struct field */
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT);
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Bail out if there are errors */
|
||||
if (NeedClean <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for a bit-field declaration */
|
||||
FieldWidth = ParseFieldWidth (&Decl);
|
||||
|
@ -1123,9 +1051,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
|||
Decl.Type[0].C &= ~T_QUAL_CVR;
|
||||
}
|
||||
} else {
|
||||
/* A non bit-field without a name is legal but useless */
|
||||
Warning ("Declaration does not declare anything");
|
||||
|
||||
/* Invalid member */
|
||||
goto NextMember;
|
||||
}
|
||||
} else if (FieldWidth > 0) {
|
||||
|
@ -1160,7 +1086,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
|||
** bit-field.
|
||||
*/
|
||||
AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth,
|
||||
SignednessSpecified);
|
||||
(Spec.Flags & DS_EXPLICIT_SIGNEDNESS) != 0);
|
||||
} else if (Decl.Ident[0] != '\0') {
|
||||
/* Add the new field to the table */
|
||||
Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0);
|
||||
|
@ -1189,17 +1115,23 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
|||
}
|
||||
}
|
||||
|
||||
NextMember: if (CurTok.Tok != TOK_COMMA) {
|
||||
NextMember:
|
||||
/* Check for end of declaration list */
|
||||
if (CurTok.Tok != TOK_COMMA) {
|
||||
break;
|
||||
}
|
||||
Spec.Flags |= DS_NO_EMPTY_DECL;
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
/* Must be followed by a semicolon */
|
||||
if (NeedClean >= 0 && ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
EndOfDecl:
|
||||
if (NeedClean > 0) {
|
||||
/* Must be followed by a semicolon */
|
||||
if (ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try some smart error recovery */
|
||||
|
@ -1220,11 +1152,6 @@ NextMember: if (CurTok.Tok != TOK_COMMA) {
|
|||
Flags |= SC_FICTITIOUS;
|
||||
}
|
||||
|
||||
/* Empty union is not supported now */
|
||||
if (UnionSize == 0) {
|
||||
Error ("Empty union type '%s' is not supported", Name);
|
||||
}
|
||||
|
||||
/* Make a real entry from the forward decl and return it */
|
||||
return AddStructSym (Name, SC_UNION | SC_DEF | Flags, UnionSize, FieldTab, DSFlags);
|
||||
}
|
||||
|
@ -1270,9 +1197,15 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
|||
|
||||
/* Get the type of the entry */
|
||||
DeclSpec Spec;
|
||||
int SignednessSpecified = 0;
|
||||
int NeedClean = 0;
|
||||
|
||||
/* Check for extra semicolons */
|
||||
if (CurTok.Tok == TOK_SEMI) {
|
||||
/* TODO: warn on this if we have a pedantic mode */
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for a _Static_assert */
|
||||
if (CurTok.Tok == TOK_STATIC_ASSERT) {
|
||||
ParseStaticAssert ();
|
||||
|
@ -1280,7 +1213,27 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
|||
}
|
||||
|
||||
InitDeclSpec (&Spec);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
||||
|
||||
/* Check if this is only a type declaration */
|
||||
if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
CheckEmptyDecl (&Spec);
|
||||
NextToken ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we haven't got a type specifier yet, something must be wrong */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* Avoid extra errors if it was a failed type specifier */
|
||||
if ((Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Error ("Declaration specifier expected");
|
||||
}
|
||||
NeedClean = -1;
|
||||
goto EndOfDecl;
|
||||
}
|
||||
|
||||
/* Allow anonymous bit-fields */
|
||||
Spec.Flags |= DS_ALLOW_BITFIELD;
|
||||
|
||||
/* Read fields with this type */
|
||||
while (1) {
|
||||
|
@ -1296,7 +1249,12 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
|||
}
|
||||
|
||||
/* Get type and name of the struct field */
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT);
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Bail out if there are errors */
|
||||
if (NeedClean <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for a bit-field declaration */
|
||||
FieldWidth = ParseFieldWidth (&Decl);
|
||||
|
@ -1340,9 +1298,7 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
|||
Decl.Type[0].C &= ~T_QUAL_CVR;
|
||||
}
|
||||
} else {
|
||||
/* A non bit-field without a name is legal but useless */
|
||||
Warning ("Declaration does not declare anything");
|
||||
|
||||
/* Invalid member */
|
||||
goto NextMember;
|
||||
}
|
||||
} else if (FieldWidth > 0) {
|
||||
|
@ -1392,8 +1348,8 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
|||
** bit-field as a char type in expressions.
|
||||
*/
|
||||
CHECK (BitOffs < CHAR_BITS);
|
||||
AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs,
|
||||
FieldWidth, SignednessSpecified);
|
||||
AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs, FieldWidth,
|
||||
(Spec.Flags & DS_EXPLICIT_SIGNEDNESS) != 0);
|
||||
BitOffs += FieldWidth;
|
||||
CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type));
|
||||
/* Add any full bytes to the struct size */
|
||||
|
@ -1432,17 +1388,23 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
|||
}
|
||||
}
|
||||
|
||||
NextMember: if (CurTok.Tok != TOK_COMMA) {
|
||||
NextMember:
|
||||
/* Check for end of declaration list */
|
||||
if (CurTok.Tok != TOK_COMMA) {
|
||||
break;
|
||||
}
|
||||
Spec.Flags |= DS_NO_EMPTY_DECL;
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
/* Must be followed by a semicolon */
|
||||
if (NeedClean >= 0 && ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
EndOfDecl:
|
||||
if (NeedClean > 0) {
|
||||
/* Must be followed by a semicolon */
|
||||
if (ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try some smart error recovery */
|
||||
|
@ -1471,18 +1433,13 @@ NextMember: if (CurTok.Tok != TOK_COMMA) {
|
|||
Flags |= SC_FICTITIOUS;
|
||||
}
|
||||
|
||||
/* Empty struct is not supported now */
|
||||
if (StructSize == 0) {
|
||||
Error ("Empty struct type '%s' is not supported", Name);
|
||||
}
|
||||
|
||||
/* Make a real entry from the forward decl and return it */
|
||||
return AddStructSym (Name, SC_STRUCT | SC_DEF | Flags, StructSize, FieldTab, DSFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSpecified)
|
||||
static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags)
|
||||
/* Parse a type specifier. Store whether one of "signed" or "unsigned" was
|
||||
** specified, so bit-fields of unspecified signedness can be treated as
|
||||
** unsigned; without special handling, it would be treated as signed.
|
||||
|
@ -1492,12 +1449,8 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
SymEntry* TagEntry;
|
||||
TypeCode Qualifiers = T_QUAL_NONE;
|
||||
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 0;
|
||||
}
|
||||
|
||||
/* Assume we have an explicit type */
|
||||
Spec->Flags &= ~DS_DEF_TYPE;
|
||||
/* Assume we have an explicitly specified type */
|
||||
Spec->Flags = (Spec->Flags & ~DS_TYPE_MASK) | DS_EXPLICIT_TYPE;
|
||||
|
||||
/* Read storage specifiers and/or type qualifiers if we have any */
|
||||
OptionalSpecifiers (Spec, &Qualifiers, TSFlags);
|
||||
|
@ -1521,15 +1474,13 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
case TOK_LONG:
|
||||
NextToken ();
|
||||
if (CurTok.Tok == TOK_UNSIGNED) {
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
NextToken ();
|
||||
OptionalInt ();
|
||||
Spec->Type[0].C = T_ULONG;
|
||||
Spec->Type[1].C = T_END;
|
||||
} else {
|
||||
OptionalSigned (SignednessSpecified);
|
||||
OptionalSigned (Spec);
|
||||
OptionalInt ();
|
||||
Spec->Type[0].C = T_LONG;
|
||||
Spec->Type[1].C = T_END;
|
||||
|
@ -1539,15 +1490,13 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
case TOK_SHORT:
|
||||
NextToken ();
|
||||
if (CurTok.Tok == TOK_UNSIGNED) {
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
NextToken ();
|
||||
OptionalInt ();
|
||||
Spec->Type[0].C = T_USHORT;
|
||||
Spec->Type[1].C = T_END;
|
||||
} else {
|
||||
OptionalSigned (SignednessSpecified);
|
||||
OptionalSigned (Spec);
|
||||
OptionalInt ();
|
||||
Spec->Type[0].C = T_SHORT;
|
||||
Spec->Type[1].C = T_END;
|
||||
|
@ -1560,10 +1509,8 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
Spec->Type[1].C = T_END;
|
||||
break;
|
||||
|
||||
case TOK_SIGNED:
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
case TOK_SIGNED:
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
NextToken ();
|
||||
switch (CurTok.Tok) {
|
||||
|
||||
|
@ -1599,9 +1546,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
break;
|
||||
|
||||
case TOK_UNSIGNED:
|
||||
if (SignednessSpecified != NULL) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
NextToken ();
|
||||
switch (CurTok.Tok) {
|
||||
|
||||
|
@ -1650,15 +1595,19 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
|
||||
case TOK_UNION:
|
||||
NextToken ();
|
||||
/* */
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Check for tag name */
|
||||
if (CurTok.Tok == TOK_IDENT) {
|
||||
strcpy (Ident, CurTok.Ident);
|
||||
NextToken ();
|
||||
} else {
|
||||
} else if (CurTok.Tok == TOK_LCURLY) {
|
||||
AnonName (Ident, "union");
|
||||
} else {
|
||||
Error ("Tag name identifier or '{' expected");
|
||||
UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE);
|
||||
break;
|
||||
}
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Declare the union in the current scope */
|
||||
TagEntry = ParseUnionSpec (Ident, &Spec->Flags);
|
||||
/* Encode the union entry into the type */
|
||||
|
@ -1669,15 +1618,19 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
|
||||
case TOK_STRUCT:
|
||||
NextToken ();
|
||||
/* */
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Check for tag name */
|
||||
if (CurTok.Tok == TOK_IDENT) {
|
||||
strcpy (Ident, CurTok.Ident);
|
||||
NextToken ();
|
||||
} else {
|
||||
} else if (CurTok.Tok == TOK_LCURLY) {
|
||||
AnonName (Ident, "struct");
|
||||
} else {
|
||||
Error ("Tag name identifier or '{' expected");
|
||||
UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE);
|
||||
break;
|
||||
}
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Declare the struct in the current scope */
|
||||
TagEntry = ParseStructSpec (Ident, &Spec->Flags);
|
||||
/* Encode the struct entry into the type */
|
||||
|
@ -1688,18 +1641,19 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
|
||||
case TOK_ENUM:
|
||||
NextToken ();
|
||||
/* Named enum */
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Check for tag name */
|
||||
if (CurTok.Tok == TOK_IDENT) {
|
||||
strcpy (Ident, CurTok.Ident);
|
||||
NextToken ();
|
||||
} else {
|
||||
if (CurTok.Tok != TOK_LCURLY) {
|
||||
Error ("Identifier expected for enum tag name");
|
||||
}
|
||||
} else if (CurTok.Tok == TOK_LCURLY) {
|
||||
AnonName (Ident, "enum");
|
||||
} else {
|
||||
Error ("Tag name identifier or '{' expected");
|
||||
UseDefaultType (Spec, TS_DEFAULT_TYPE_NONE);
|
||||
break;
|
||||
}
|
||||
/* Remember we have an extra type decl */
|
||||
Spec->Flags |= DS_EXTRA_TYPE;
|
||||
/* Parse the enum decl */
|
||||
TagEntry = ParseEnumSpec (Ident, &Spec->Flags);
|
||||
/* Encode the enum entry into the type */
|
||||
|
@ -1709,9 +1663,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
/* The signedness of enums is determined by the type, so say this is specified to avoid
|
||||
** the int -> unsigned int handling for plain int bit-fields in AddBitField.
|
||||
*/
|
||||
if (SignednessSpecified) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
break;
|
||||
|
||||
case TOK_IDENT:
|
||||
|
@ -1728,9 +1680,7 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
** Unforunately, this will cause plain int bit-fields defined via typedefs
|
||||
** to be treated as signed rather than unsigned.
|
||||
*/
|
||||
if (SignednessSpecified) {
|
||||
*SignednessSpecified = 1;
|
||||
}
|
||||
Spec->Flags |= DS_EXPLICIT_SIGNEDNESS;
|
||||
break;
|
||||
} else if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) {
|
||||
/* Treat this identifier as an unknown type */
|
||||
|
@ -1744,23 +1694,13 @@ static void ParseTypeSpec (DeclSpec* Spec, typespec_t TSFlags, int* SignednessSp
|
|||
** in DeclareLocals. The type code used here doesn't matter as
|
||||
** long as it has no qualifiers.
|
||||
*/
|
||||
Spec->Flags |= DS_DEF_TYPE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
UseDefaultType (Spec, TS_DEFAULT_TYPE_INT);
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
|
||||
default:
|
||||
if ((TSFlags & TS_MASK_DEFAULT_TYPE) == TS_DEFAULT_TYPE_NONE) {
|
||||
Spec->Flags |= DS_NO_TYPE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
} else {
|
||||
Spec->Flags |= DS_DEF_TYPE;
|
||||
Spec->Type[0].C = T_INT;
|
||||
Spec->Type[1].C = T_END;
|
||||
}
|
||||
UseDefaultType (Spec, TSFlags);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1849,6 +1789,9 @@ static void ParseOldStyleParamList (FuncDesc* F)
|
|||
/* Read the declaration specifier */
|
||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
|
||||
|
||||
/* Paremeters must have identifiers as names */
|
||||
Spec.Flags |= DS_NO_EMPTY_DECL;
|
||||
|
||||
/* We accept only auto and register as storage class specifiers, but
|
||||
** we ignore all this, since we use auto anyway.
|
||||
*/
|
||||
|
@ -1858,7 +1801,7 @@ static void ParseOldStyleParamList (FuncDesc* F)
|
|||
}
|
||||
|
||||
/* Type must be specified */
|
||||
if ((Spec.Flags & DS_NO_TYPE) != 0) {
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
Error ("Expected declaration specifiers");
|
||||
break;
|
||||
}
|
||||
|
@ -1869,7 +1812,7 @@ static void ParseOldStyleParamList (FuncDesc* F)
|
|||
Declarator Decl;
|
||||
|
||||
/* Read the parameter */
|
||||
ParseDecl (&Spec, &Decl, DM_NEED_IDENT);
|
||||
ParseDecl (&Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Warn about new local type declaration */
|
||||
if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) {
|
||||
|
@ -1951,7 +1894,7 @@ static void ParseAnsiParamList (FuncDesc* F)
|
|||
}
|
||||
|
||||
/* Type must be specified */
|
||||
if ((Spec.Flags & DS_NO_TYPE) != 0) {
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
Error ("Type specifier missing");
|
||||
}
|
||||
|
||||
|
@ -2093,7 +2036,7 @@ static FuncDesc* ParseFuncDecl (void)
|
|||
|
||||
|
||||
|
||||
static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
/* Recursively process direct declarators. Build a type array in reverse order. */
|
||||
{
|
||||
/* Read optional function or pointer qualifiers that modify the identifier
|
||||
|
@ -2111,19 +2054,19 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
|||
NextToken ();
|
||||
|
||||
/* A pointer type cannot be used as an empty declaration */
|
||||
if (Mode == DM_ACCEPT_IDENT) {
|
||||
Mode = DM_NEED_IDENT;
|
||||
if (Mode == DM_IDENT_OR_EMPTY) {
|
||||
Spec->Flags |= DS_NO_EMPTY_DECL;
|
||||
}
|
||||
|
||||
/* Allow const, restrict, and volatile qualifiers */
|
||||
Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR);
|
||||
|
||||
/* Parse the type that the pointer points to */
|
||||
Mode = DirectDecl (Spec, D, Mode);
|
||||
DirectDecl (Spec, D, Mode);
|
||||
|
||||
/* Add the type */
|
||||
AddTypeCodeToDeclarator (D, T_PTR | Qualifiers);
|
||||
return Mode;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CurTok.Tok == TOK_LPAREN) {
|
||||
|
@ -2131,28 +2074,27 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
|||
/* An empty declaration cannot contain parentheses where an identifier
|
||||
** would show up if it were a non-empty declaration.
|
||||
*/
|
||||
if (Mode == DM_ACCEPT_IDENT) {
|
||||
Mode = DM_NEED_IDENT;
|
||||
if (Mode == DM_IDENT_OR_EMPTY) {
|
||||
Spec->Flags |= DS_NO_EMPTY_DECL;
|
||||
}
|
||||
Mode = DirectDecl (Spec, D, Mode);
|
||||
DirectDecl (Spec, D, Mode);
|
||||
ConsumeRParen ();
|
||||
} else if (CurTok.Tok == TOK_IDENT) {
|
||||
if (Mode == DM_NO_IDENT) {
|
||||
Error ("Unexpected identifier in type name");
|
||||
}
|
||||
strcpy (D->Ident, CurTok.Ident);
|
||||
NextToken ();
|
||||
} else {
|
||||
D->Ident[0] = '\0';
|
||||
if (Mode == DM_NEED_IDENT) {
|
||||
if ((Spec->Flags & DS_NO_EMPTY_DECL) != 0 &&
|
||||
CurTok.Tok != TOK_LBRACK &&
|
||||
((Spec->Flags & DS_ALLOW_BITFIELD) == 0 || CurTok.Tok != TOK_COLON)) {
|
||||
Error ("Identifier expected");
|
||||
}
|
||||
}
|
||||
|
||||
while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) {
|
||||
/* An array or function type cannot be used as an empty declaration */
|
||||
if (Mode == DM_ACCEPT_IDENT && D->Ident[0] == '\0') {
|
||||
Mode = DM_NEED_IDENT;
|
||||
Error ("Identifier expected");
|
||||
}
|
||||
|
||||
if (CurTok.Tok == TOK_LPAREN) {
|
||||
|
||||
/* Function declarator */
|
||||
|
@ -2191,6 +2133,18 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
|||
/* Array declarator */
|
||||
long Size = UNSPECIFIED;
|
||||
|
||||
/* An array type cannot be used as an empty declaration */
|
||||
if (Mode == DM_IDENT_OR_EMPTY) {
|
||||
Spec->Flags |= DS_NO_EMPTY_DECL;
|
||||
if (D->Ident[0] == '\0') {
|
||||
if ((Spec->Flags & DS_TYPE_MASK) != DS_NONE) {
|
||||
Error ("Identifier or ';' expected after declaration specifiers");
|
||||
} else {
|
||||
Error ("Identifier expected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We cannot have any qualifiers for an array */
|
||||
if (Qualifiers != T_QUAL_NONE) {
|
||||
Error ("Invalid qualifiers for array");
|
||||
|
@ -2238,8 +2192,6 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
|||
if (Qualifiers & T_QUAL_CDECL) {
|
||||
Error ("Invalid '__cdecl__' qualifier");
|
||||
}
|
||||
|
||||
return Mode;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2251,20 +2203,41 @@ static declmode_t DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mo
|
|||
|
||||
|
||||
Type* ParseType (Type* T)
|
||||
/* Parse a complete type specification */
|
||||
/* Parse a complete type specification in parentheses */
|
||||
{
|
||||
DeclSpec Spec;
|
||||
Declarator Decl;
|
||||
int NeedClean = -1;
|
||||
|
||||
/* Skip the left paren */
|
||||
NextToken ();
|
||||
|
||||
/* Get a type without a default */
|
||||
InitDeclSpec (&Spec);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, NULL);
|
||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
||||
|
||||
/* Parse additional declarators */
|
||||
ParseDecl (&Spec, &Decl, DM_NO_IDENT);
|
||||
/* Only parse further if there is a type specifier */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) {
|
||||
/* Parse additional declarators */
|
||||
NeedClean = ParseDecl (&Spec, &Decl, DM_NO_IDENT);
|
||||
|
||||
/* Copy the type to the target buffer */
|
||||
TypeCopy (T, Decl.Type);
|
||||
/* Copy the type to the target buffer */
|
||||
TypeCopy (T, Decl.Type);
|
||||
} else {
|
||||
/* Fail-safe */
|
||||
TypeCopy (T, type_int);
|
||||
}
|
||||
|
||||
/* Try some smart error recovery */
|
||||
if (NeedClean < 0) {
|
||||
SimpleErrorSkip ();
|
||||
}
|
||||
|
||||
/* Closing paren */
|
||||
if (!ConsumeRParen ()) {
|
||||
SimpleErrorSkip ();
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
/* Return a pointer to the target buffer */
|
||||
return T;
|
||||
|
@ -2272,14 +2245,24 @@ Type* ParseType (Type* T)
|
|||
|
||||
|
||||
|
||||
int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
||||
/* Parse a variable, type or function declarator. Return -1 if this stops at
|
||||
** an unpaired right parenthesis/bracket/curly brace.
|
||||
** an unpaired right parenthesis/bracket/curly brace. Return 0 if this stops
|
||||
** after consuming a semicolon or closing curly brace, or reaching an EOF.
|
||||
** Return 1 otherwise.
|
||||
*/
|
||||
{
|
||||
/* Used to check if we have any errors during parsing this */
|
||||
unsigned PrevErrorCount = ErrorCount;
|
||||
|
||||
/* If there is no explicit type specifier, an optional identifier becomes
|
||||
** required.
|
||||
*/
|
||||
if (Mode == DM_IDENT_OR_EMPTY &&
|
||||
(Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) {
|
||||
Spec->Flags |= DS_NO_EMPTY_DECL;
|
||||
}
|
||||
|
||||
/* Initialize the Declarator struct */
|
||||
InitDeclarator (D);
|
||||
|
||||
|
@ -2293,6 +2276,11 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
|||
/* Use the storage class from the declspec */
|
||||
D->StorageClass = Spec->StorageClass;
|
||||
|
||||
/* If we have a function, add a special symbol type */
|
||||
if (IsTypeFunc (D->Type)) {
|
||||
D->StorageClass |= SC_FUNC;
|
||||
}
|
||||
|
||||
/* Do several fixes on qualifiers */
|
||||
FixQualifiers (D->Type);
|
||||
|
||||
|
@ -2307,26 +2295,8 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
|||
/* Parse attributes for this declarator */
|
||||
ParseAttribute (D);
|
||||
|
||||
/* If we have a function, add a special storage class */
|
||||
if (IsTypeFunc (D->Type)) {
|
||||
|
||||
D->StorageClass |= SC_FUNC;
|
||||
|
||||
} else if (!IsTypeVoid (D->Type)) {
|
||||
/* Check the size of the generated type */
|
||||
unsigned Size = SizeOf (D->Type);
|
||||
|
||||
if (Size >= 0x10000) {
|
||||
if (D->Ident[0] != '\0') {
|
||||
Error ("Size of '%s' is invalid (0x%06X)", D->Ident, Size);
|
||||
} else {
|
||||
Error ("Invalid size in declaration (0x%06X)", Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check a few pre-C99 things */
|
||||
if (D->Ident[0] != '\0' && (Spec->Flags & DS_DEF_TYPE) != 0) {
|
||||
if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) {
|
||||
/* Check and warn about an implicit int return in the function */
|
||||
if (IsTypeFunc (D->Type) && IsRankInt (GetFuncReturnType (D->Type))) {
|
||||
/* Function has an implicit int return. Output a warning if we don't
|
||||
|
@ -2341,7 +2311,7 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
|||
/* For anything that is not a function or typedef, check for an implicit
|
||||
** int declaration.
|
||||
*/
|
||||
if ((D->StorageClass & SC_FUNC) != SC_FUNC &&
|
||||
if (!IsTypeFunc (D->Type) &&
|
||||
(D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
|
||||
/* If the standard was not set explicitly to C89, print a warning
|
||||
** for variables with implicit int type.
|
||||
|
@ -2352,11 +2322,32 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
|||
}
|
||||
}
|
||||
|
||||
if (PrevErrorCount != ErrorCount) {
|
||||
if ((Spec->Flags & DS_DEF_TYPE) == 0 && Mode == DM_NEED_IDENT && D->Ident[0] == '\0') {
|
||||
/* Make the declaration fictitious if is is not parsed correctly */
|
||||
D->StorageClass |= SC_FICTITIOUS;
|
||||
/* Check the size of the declared type */
|
||||
if (IsObjectType (D->Type)) {
|
||||
unsigned Size = SizeOf (D->Type);
|
||||
|
||||
if (Size >= 0x10000) {
|
||||
if (D->Ident[0] != '\0') {
|
||||
Error ("Size of '%s' is too large (0x%06X)", D->Ident, Size);
|
||||
} else {
|
||||
Error ("Size in declaration is too large (0x%06X)", Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* An empty declaration must be terminated with a semicolon */
|
||||
if (PrevErrorCount == ErrorCount &&
|
||||
Mode == DM_IDENT_OR_EMPTY &&
|
||||
D->Ident[0] == '\0' &&
|
||||
CurTok.Tok != TOK_SEMI &&
|
||||
((Spec->Flags & DS_ALLOW_BITFIELD) == 0 || CurTok.Tok != TOK_COLON)) {
|
||||
Error ("Identifier or ';' expected after declaration specifiers");
|
||||
}
|
||||
|
||||
if (PrevErrorCount != ErrorCount) {
|
||||
if ((Spec->Flags & DS_TYPE_MASK) != DS_DEF_TYPE &&
|
||||
(Spec->Flags & DS_NO_EMPTY_DECL) != 0 &&
|
||||
D->Ident[0] == '\0') {
|
||||
/* Use a fictitious name for the identifier if it is missing */
|
||||
const char* Level = "";
|
||||
|
||||
|
@ -2376,15 +2367,21 @@ int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
|||
break;
|
||||
}
|
||||
AnonName (D->Ident, Level);
|
||||
|
||||
/* Make the declarator fictitious */
|
||||
D->StorageClass |= SC_FICTITIOUS;
|
||||
}
|
||||
|
||||
/* Try some smart error recovery */
|
||||
if (CurTok.Tok != TOK_LCURLY || !IsTypeFunc (D->Type)) {
|
||||
return SmartErrorSkip (0);
|
||||
/* Skip to the end of the whole declaration if it is not part of a
|
||||
** parameter list or a type cast.
|
||||
*/
|
||||
return SmartErrorSkip (Mode == DM_IDENT_OR_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2399,7 +2396,7 @@ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage)
|
|||
Spec->Flags &= ~DS_DEF_STORAGE;
|
||||
|
||||
/* Parse the type specifiers */
|
||||
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL);
|
||||
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC);
|
||||
|
||||
/* If no explicit storage class is given, use the default */
|
||||
if (Spec->StorageClass == 0) {
|
||||
|
@ -2416,7 +2413,13 @@ void CheckEmptyDecl (const DeclSpec* Spec)
|
|||
** warning if not.
|
||||
*/
|
||||
{
|
||||
if ((Spec->Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Warning ("Useless declaration");
|
||||
if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* No declaration at all */
|
||||
} else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Warning ("Declaration does not declare anything");
|
||||
} else if (IsClassStruct (Spec->Type) &&
|
||||
!IsIncompleteESUType (Spec->Type) &&
|
||||
SymHasAnonName (GetESUTagSym (Spec->Type))) {
|
||||
Warning ("Unnamed %s that defines no instances", GetBasicTypeName (Spec->Type));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,18 +65,26 @@ enum typespec_t {
|
|||
TS_DEFAULT_TYPE_AUTO = 0x02, /* C23 type inference with auto */
|
||||
|
||||
/* Whether to allow certain kinds of specifiers */
|
||||
TS_STORAGE_CLASS_SPEC = 0x04, /* Allow storage storage class specifiers */
|
||||
TS_FUNCTION_SPEC = 0x08, /* Allow function specifiers */
|
||||
TS_STORAGE_CLASS_SPEC = 0x04, /* Allow storage class specifiers */
|
||||
TS_FUNCTION_SPEC = 0x08, /* Allow function specifiers */
|
||||
};
|
||||
|
||||
/* Masks for the Flags field in DeclSpec */
|
||||
#define DS_NONE 0x0000U /* Nothing specified or used */
|
||||
#define DS_DEF_STORAGE 0x0001U /* Default storage class used */
|
||||
#define DS_NO_TYPE 0x0002U /* No type explicitly specified */
|
||||
#define DS_DEF_TYPE 0x0006U /* Default type used */
|
||||
#define DS_EXTRA_TYPE 0x0008U /* Extra type declared */
|
||||
#define DS_EXPLICIT_TYPE 0x0002U /* Type specified */
|
||||
#define DS_DEF_TYPE 0x0004U /* Implicit type used */
|
||||
#define DS_AUTO_TYPE 0x0006U /* C23 auto type used */
|
||||
#define DS_TYPE_MASK 0x0006U /* Mask for type of spec decl */
|
||||
#define DS_EXTRA_TYPE 0x0008U /* ESU type in declaration */
|
||||
#define DS_NEW_TYPE_DECL 0x0010U /* New type declared */
|
||||
#define DS_NEW_TYPE_DEF 0x0020U /* New type defined */
|
||||
#define DS_NEW_TYPE (DS_NEW_TYPE_DECL | DS_NEW_TYPE_DEF)
|
||||
#define DS_EXPLICIT_SIGNEDNESS 0x0040U /* Signedness specified */
|
||||
#define DS_NO_EMPTY_DECL 0x0100U /* Disallow empty declaration */
|
||||
#define DS_ALLOW_BITFIELD 0x0200U /* Allow anonymous bit-fields */
|
||||
|
||||
|
||||
|
||||
/* Result of ParseDeclSpec */
|
||||
typedef struct DeclSpec DeclSpec;
|
||||
|
@ -99,24 +107,22 @@ struct Declarator {
|
|||
};
|
||||
|
||||
/* Modes for ParseDecl:
|
||||
** - DM_NEED_IDENT means:
|
||||
** we *must* have a type and a variable identifer.
|
||||
** - DM_IDENT_OR_EMPTY means:
|
||||
** we *may* have an identifier, or none. If it is the latter case,
|
||||
** the type specifier must be used for an empty declaration,
|
||||
** or it is an error.
|
||||
** - DM_NO_IDENT means:
|
||||
** we must have a type but no variable identifer
|
||||
** (if there is one, it's not read).
|
||||
** - DM_ACCEPT_IDENT means:
|
||||
** we *may* have an identifier, or none. If it is the latter case,
|
||||
** the type must be used as an empty declaration, or it is an error.
|
||||
** Note: this is used for struct/union members.
|
||||
** - DM_IGNORE_IDENT means:
|
||||
** Note: this is used for type names.
|
||||
** - DM_ACCEPT_PARAM_IDENT means:
|
||||
** we *may* have an identifier. If there is an identifier,
|
||||
** it is read, but it is no error, if there is none.
|
||||
** Note: this is used for function parameter type lists.
|
||||
*/
|
||||
typedef enum {
|
||||
DM_NEED_IDENT,
|
||||
DM_IDENT_OR_EMPTY,
|
||||
DM_NO_IDENT,
|
||||
DM_ACCEPT_IDENT,
|
||||
DM_ACCEPT_PARAM_IDENT,
|
||||
} declmode_t;
|
||||
|
||||
|
@ -128,29 +134,14 @@ typedef enum {
|
|||
|
||||
|
||||
|
||||
int SmartErrorSkip (int WholeDecl);
|
||||
/* Try some smart error recovery.
|
||||
**
|
||||
** - If WholeDecl is 0:
|
||||
** Skip tokens until a comma or closing curly brace that is not enclosed in
|
||||
** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or
|
||||
** unpaired right parenthesis/bracket/curly brace is reached.
|
||||
**
|
||||
** - If WholeDecl is non-0:
|
||||
** Skip tokens until a closing curly brace that is not enclosed in an open
|
||||
** parenthesis/bracket/curly brace, or until a semicolon or EOF is reached.
|
||||
**
|
||||
** Return 0 if this exits as soon as it reaches an EOF. Return 0 as well if
|
||||
** this exits with no open parentheses/brackets/curly braces. Otherwise, return
|
||||
** -1.
|
||||
*/
|
||||
|
||||
Type* ParseType (Type* Type);
|
||||
/* Parse a complete type specification */
|
||||
/* Parse a complete type specification in parentheses */
|
||||
|
||||
int ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode);
|
||||
int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode);
|
||||
/* Parse a variable, type or function declarator. Return -1 if this stops at
|
||||
** an unpaired right parenthesis/bracket/curly brace.
|
||||
** an unpaired right parenthesis/bracket/curly brace. Return 0 if this stops
|
||||
** after consuming a semicolon or closing curly brace, or reaching an EOF.
|
||||
** Return 1 otherwise.
|
||||
*/
|
||||
|
||||
void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage);
|
||||
|
|
|
@ -1414,25 +1414,9 @@ static void Primary (ExprDesc* E)
|
|||
DeclSpec Spec;
|
||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
|
||||
|
||||
if ((Spec.Flags & DS_DEF_TYPE) == 0) {
|
||||
/* Recognized but not supported */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) {
|
||||
Error ("Mixed declarations and code are not supported in cc65");
|
||||
|
||||
while (CurTok.Tok != TOK_SEMI) {
|
||||
Declarator Decl;
|
||||
|
||||
/* Parse one declaration */
|
||||
ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT);
|
||||
if (CurTok.Tok == TOK_ASSIGN) {
|
||||
NextToken ();
|
||||
ParseInit (Decl.Type);
|
||||
}
|
||||
if (CurTok.Tok == TOK_COMMA) {
|
||||
NextToken ();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
SmartErrorSkip (0);
|
||||
} else {
|
||||
Error ("Expression expected");
|
||||
E->Flags |= E_EVAL_MAYBE_UNUSED;
|
||||
|
@ -2089,9 +2073,7 @@ void hie10 (ExprDesc* Expr)
|
|||
NextToken ();
|
||||
if (TypeSpecAhead ()) {
|
||||
Type T[MAXTYPELEN];
|
||||
NextToken ();
|
||||
Size = ExprCheckedSizeOf (ParseType (T));
|
||||
ConsumeRParen ();
|
||||
} else {
|
||||
/* Remember the output queue pointer */
|
||||
CodeMark Mark;
|
||||
|
@ -4102,9 +4084,10 @@ static void hieQuest (ExprDesc* Expr)
|
|||
/* Avoid further errors */
|
||||
ResultType = NewPointerTo (type_void);
|
||||
} else {
|
||||
/* Result has the composite type */
|
||||
/* Result has the properly qualified composite type */
|
||||
ResultType = TypeDup (Expr2.Type);
|
||||
TypeComposition (ResultType, Expr3.Type);
|
||||
ResultType[1].C |= GetQualifier (Indirect (Expr3.Type));
|
||||
}
|
||||
}
|
||||
} else if (IsClassPtr (Expr2.Type) && Expr3IsNULL) {
|
||||
|
|
|
@ -441,14 +441,15 @@ static void ParseStaticDecl (Declarator* Decl)
|
|||
|
||||
|
||||
|
||||
static void ParseOneDecl (const DeclSpec* Spec)
|
||||
static int ParseOneDecl (DeclSpec* Spec)
|
||||
/* Parse one variable declarator. */
|
||||
{
|
||||
Declarator Decl; /* Declarator data structure */
|
||||
Declarator Decl; /* Declarator data structure */
|
||||
int NeedClean;
|
||||
|
||||
|
||||
/* Read the declarator */
|
||||
ParseDecl (Spec, &Decl, DM_NEED_IDENT);
|
||||
NeedClean = ParseDecl (Spec, &Decl, DM_IDENT_OR_EMPTY);
|
||||
|
||||
/* Check if there are any non-extern storage classes set for function
|
||||
** declarations. Function can only be declared inside functions with the
|
||||
|
@ -538,6 +539,8 @@ static void ParseOneDecl (const DeclSpec* Spec)
|
|||
|
||||
/* Make sure we aren't missing some work */
|
||||
CheckDeferredOpAllDone ();
|
||||
|
||||
return NeedClean;
|
||||
}
|
||||
|
||||
|
||||
|
@ -553,15 +556,8 @@ void DeclareLocals (void)
|
|||
|
||||
/* Loop until we don't find any more variables */
|
||||
while (1) {
|
||||
|
||||
/* Check variable declarations. We need to distinguish between a
|
||||
** default int type and the end of variable declarations. So we
|
||||
** will do the following: If there is no explicit storage class
|
||||
** specifier *and* no explicit type given, *and* no type qualifiers
|
||||
** have been read, it is assumed that we have reached the end of
|
||||
** declarations.
|
||||
*/
|
||||
DeclSpec Spec;
|
||||
int NeedClean;
|
||||
|
||||
/* Check for a _Static_assert */
|
||||
if (CurTok.Tok == TOK_STATIC_ASSERT) {
|
||||
|
@ -569,10 +565,18 @@ void DeclareLocals (void)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Read the declaration specifier */
|
||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO);
|
||||
if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
|
||||
(Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */
|
||||
GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
|
||||
|
||||
/* Check variable declarations. We need distinguish between a default
|
||||
** int type and the end of variable declarations. So we will do the
|
||||
** following: If there is no explicit storage class specifier *and* no
|
||||
** explicit type given, *and* no type qualifiers have been read, it is
|
||||
** assumed that we have reached the end of declarations.
|
||||
*/
|
||||
if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
|
||||
(Spec.Flags & DS_TYPE_MASK) == DS_DEF_TYPE && /* No type given */
|
||||
GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -584,11 +588,24 @@ void DeclareLocals (void)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* If we haven't got a type specifier yet, something must be wrong */
|
||||
if ((Spec.Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||
/* Avoid extra errors if it was a failed type specifier */
|
||||
if ((Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
||||
Error ("Declaration specifier expected");
|
||||
}
|
||||
NeedClean = -1;
|
||||
goto EndOfDecl;
|
||||
}
|
||||
|
||||
/* Parse a comma separated variable list */
|
||||
while (1) {
|
||||
|
||||
/* Parse one declaration */
|
||||
ParseOneDecl (&Spec);
|
||||
/* Parse one declarator */
|
||||
NeedClean = ParseOneDecl (&Spec);
|
||||
if (NeedClean <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if there is more */
|
||||
if (CurTok.Tok == TOK_COMMA) {
|
||||
|
@ -600,8 +617,20 @@ void DeclareLocals (void)
|
|||
}
|
||||
}
|
||||
|
||||
/* A semicolon must follow */
|
||||
ConsumeSemi ();
|
||||
if (NeedClean > 0) {
|
||||
/* Must be followed by a semicolon */
|
||||
if (ConsumeSemi ()) {
|
||||
NeedClean = 0;
|
||||
} else {
|
||||
NeedClean = -1;
|
||||
}
|
||||
}
|
||||
|
||||
EndOfDecl:
|
||||
/* Try some smart error recovery */
|
||||
if (NeedClean < 0) {
|
||||
SmartErrorSkip (1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Be sure to allocate any reserved space for locals */
|
||||
|
|
|
@ -1235,6 +1235,212 @@ void SkipTokens (const token_t* TokenList, unsigned TokenCount)
|
|||
|
||||
|
||||
|
||||
static void OpenBrace (Collection* C, token_t Tok)
|
||||
/* Consume an opening parenthesis/bracket/curly brace and remember that */
|
||||
{
|
||||
switch (Tok) {
|
||||
case TOK_LPAREN: Tok = TOK_RPAREN; break;
|
||||
case TOK_LBRACK: Tok = TOK_RBRACK; break;
|
||||
case TOK_LCURLY: Tok = TOK_RCURLY; break;
|
||||
default: Internal ("Unexpected opening token: %02X", (unsigned)Tok);
|
||||
}
|
||||
CollAppend (C, (void*)Tok);
|
||||
NextToken ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void PopBrace (Collection* C)
|
||||
/* Close the latest open parenthesis/bracket/curly brace */
|
||||
{
|
||||
if (CollCount (C) > 0) {
|
||||
CollPop (C);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CloseBrace (Collection* C, token_t Tok)
|
||||
/* Consume a closing parenthesis/bracket/curly brace if it is matched with an
|
||||
** opening one to close and return 0, or bail out and return -1 if it is not
|
||||
** matched.
|
||||
*/
|
||||
{
|
||||
if (CollCount (C) > 0) {
|
||||
token_t LastTok = (token_t)CollLast (C);
|
||||
if (LastTok == Tok) {
|
||||
CollPop (C);
|
||||
NextToken ();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int SmartErrorSkip (int TillEnd)
|
||||
/* Try some smart error recovery.
|
||||
**
|
||||
** - If TillEnd == 0:
|
||||
** Skip tokens until a comma or closing curly brace that is not enclosed in
|
||||
** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or
|
||||
** unpaired right parenthesis/bracket/curly brace is reached. The closing
|
||||
** curly brace is consumed in the former case.
|
||||
**
|
||||
** - If TillEnd != 0:
|
||||
** Skip tokens until a right curly brace or semicolon is reached and consumed
|
||||
** while there are no open parentheses/brackets/curly braces, or until an EOF
|
||||
** is reached anytime. Any open parenthesis/bracket/curly brace is considered
|
||||
** to be closed by consuming a right parenthesis/bracket/curly brace even if
|
||||
** they didn't match.
|
||||
**
|
||||
** - Return -1:
|
||||
** If this exits at a semicolon or unpaired right parenthesis/bracket/curly
|
||||
** brace while there are still open parentheses/brackets/curly braces.
|
||||
**
|
||||
** - Return 0:
|
||||
** If this exits as soon as it reaches an EOF;
|
||||
** Or if this exits right after consuming a semicolon or right curly brace
|
||||
** while there are no open parentheses/brackets/curly braces.
|
||||
**
|
||||
** - Return 1:
|
||||
** If this exits at a non-EOF without consuming it.
|
||||
*/
|
||||
{
|
||||
Collection C = AUTO_COLLECTION_INITIALIZER;
|
||||
int Res = 0;
|
||||
|
||||
/* Some fix point tokens that are used for error recovery */
|
||||
static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI,
|
||||
TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY };
|
||||
|
||||
while (CurTok.Tok != TOK_CEOF) {
|
||||
SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0]));
|
||||
|
||||
switch (CurTok.Tok) {
|
||||
case TOK_LPAREN:
|
||||
case TOK_LBRACK:
|
||||
case TOK_LCURLY:
|
||||
OpenBrace (&C, CurTok.Tok);
|
||||
break;
|
||||
|
||||
case TOK_RPAREN:
|
||||
case TOK_RBRACK:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
if (!TillEnd) {
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
PopBrace (&C);
|
||||
NextToken ();
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_RCURLY:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
if (!TillEnd) {
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
PopBrace (&C);
|
||||
NextToken ();
|
||||
}
|
||||
if (CollCount (&C) == 0) {
|
||||
/* We consider this as a terminator as well */
|
||||
Res = 0;
|
||||
goto ExitPoint;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_COMMA:
|
||||
if (CollCount (&C) == 0 && !TillEnd) {
|
||||
Res = 1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
break;
|
||||
|
||||
case TOK_SEMI:
|
||||
if (CollCount (&C) == 0) {
|
||||
if (TillEnd) {
|
||||
NextToken ();
|
||||
Res = 0;
|
||||
} else {
|
||||
Res = 1;
|
||||
}
|
||||
goto ExitPoint;
|
||||
}
|
||||
NextToken ();
|
||||
break;
|
||||
|
||||
case TOK_CEOF:
|
||||
/* We cannot consume this */
|
||||
Res = 0;
|
||||
goto ExitPoint;
|
||||
|
||||
default:
|
||||
Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok);
|
||||
}
|
||||
}
|
||||
|
||||
ExitPoint:
|
||||
DoneCollection (&C);
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int SimpleErrorSkip (void)
|
||||
/* Skip tokens until an EOF or unpaired right parenthesis/bracket/curly brace
|
||||
** is reached. Return 0 If this exits at an EOF. Otherwise return -1.
|
||||
*/
|
||||
{
|
||||
Collection C = AUTO_COLLECTION_INITIALIZER;
|
||||
int Res = 0;
|
||||
|
||||
/* Some fix point tokens that are used for error recovery */
|
||||
static const token_t TokenList[] = {
|
||||
TOK_LPAREN, TOK_RPAREN, TOK_LBRACK, TOK_RBRACK, TOK_LCURLY, TOK_RCURLY };
|
||||
|
||||
while (CurTok.Tok != TOK_CEOF) {
|
||||
SkipTokens (TokenList, sizeof (TokenList) / sizeof (TokenList[0]));
|
||||
|
||||
switch (CurTok.Tok) {
|
||||
case TOK_LPAREN:
|
||||
case TOK_LBRACK:
|
||||
case TOK_LCURLY:
|
||||
OpenBrace (&C, CurTok.Tok);
|
||||
break;
|
||||
|
||||
case TOK_RPAREN:
|
||||
case TOK_RBRACK:
|
||||
case TOK_RCURLY:
|
||||
if (CloseBrace (&C, CurTok.Tok) < 0) {
|
||||
/* Found a terminator */
|
||||
Res = -1;
|
||||
goto ExitPoint;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK_CEOF:
|
||||
/* We cannot go any farther */
|
||||
Res = 0;
|
||||
goto ExitPoint;
|
||||
|
||||
default:
|
||||
Internal ("Unexpected token: %02X", (unsigned)CurTok.Tok);
|
||||
}
|
||||
}
|
||||
|
||||
ExitPoint:
|
||||
DoneCollection (&C);
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int Consume (token_t Token, const char* ErrorMsg)
|
||||
/* Eat token if it is the next in the input stream, otherwise print an error
|
||||
** message. Returns true if the token was found and false otherwise.
|
||||
|
|
|
@ -310,6 +310,40 @@ void SkipTokens (const token_t* TokenList, unsigned TokenCount);
|
|||
** This routine is used for error recovery.
|
||||
*/
|
||||
|
||||
int SmartErrorSkip (int TillEnd);
|
||||
/* Try some smart error recovery.
|
||||
**
|
||||
** - If TillEnd == 0:
|
||||
** Skip tokens until a comma or closing curly brace that is not enclosed in
|
||||
** an open parenthesis/bracket/curly brace, or until a semicolon, EOF or
|
||||
** unpaired right parenthesis/bracket/curly brace is reached. The closing
|
||||
** curly brace is consumed in the former case.
|
||||
**
|
||||
** - If TillEnd != 0:
|
||||
** Skip tokens until a right curly brace or semicolon is reached and consumed
|
||||
** while there are no open parentheses/brackets/curly braces, or until an EOF
|
||||
** is reached anytime. Any open parenthesis/bracket/curly brace is considered
|
||||
** to be closed by consuming a right parenthesis/bracket/curly brace even if
|
||||
** they didn't match.
|
||||
**
|
||||
** - Return -1:
|
||||
** If this exits at a semicolon or unpaired right parenthesis/bracket/curly
|
||||
** brace while there are still open parentheses/brackets/curly braces.
|
||||
**
|
||||
** - Return 0:
|
||||
** If this exits as soon as it reaches an EOF;
|
||||
** Or if this exits right after consuming a semicolon or right curly brace
|
||||
** while there are no open parentheses/brackets/curly braces.
|
||||
**
|
||||
** - Return 1:
|
||||
** If this exits at a non-EOF without consuming it.
|
||||
*/
|
||||
|
||||
int SimpleErrorSkip (void);
|
||||
/* Skip tokens until an EOF or unpaired right parenthesis/bracket/curly brace
|
||||
** is reached. Return 0 If this exits at an EOF. Otherwise return -1.
|
||||
*/
|
||||
|
||||
int Consume (token_t Token, const char* ErrorMsg);
|
||||
/* Eat token if it is the next in the input stream, otherwise print an error
|
||||
** message. Returns true if the token was found and false otherwise.
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
|
||||
|
||||
void ParseStaticAssert ()
|
||||
void ParseStaticAssert (void)
|
||||
{
|
||||
/*
|
||||
** static_assert-declaration ::=
|
||||
|
@ -53,20 +53,23 @@ void ParseStaticAssert ()
|
|||
** _Static_assert ( constant-expression , string-literal ) ;
|
||||
*/
|
||||
ExprDesc Expr;
|
||||
int failed;
|
||||
unsigned PrevErrorCount = ErrorCount;
|
||||
int failed = 0;
|
||||
|
||||
/* Skip the _Static_assert token itself */
|
||||
CHECK (CurTok.Tok == TOK_STATIC_ASSERT);
|
||||
NextToken ();
|
||||
|
||||
/* We expect an opening paren */
|
||||
if (!ConsumeLParen ()) {
|
||||
return;
|
||||
if (ConsumeLParen ()) {
|
||||
/* Parse assertion condition */
|
||||
Expr = NoCodeConstAbsIntExpr (hie1);
|
||||
failed = !Expr.IVal;
|
||||
}
|
||||
|
||||
/* Parse assertion condition */
|
||||
Expr = NoCodeConstAbsIntExpr (hie1);
|
||||
failed = !Expr.IVal;
|
||||
if (PrevErrorCount != ErrorCount) {
|
||||
goto ExitPoint;
|
||||
}
|
||||
|
||||
/* If there is a comma, we also have an error message. The message is optional because we
|
||||
** support the C2X syntax with only an expression.
|
||||
|
@ -84,19 +87,16 @@ void ParseStaticAssert ()
|
|||
/* String literal */
|
||||
if (CurTok.Tok != TOK_SCONST) {
|
||||
Error ("String literal expected for static_assert message");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Issue an error including the message if the static_assert failed. */
|
||||
if (failed) {
|
||||
Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal));
|
||||
}
|
||||
|
||||
/* Issue an error including the message if the static_assert failed. */
|
||||
if (failed) {
|
||||
Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal));
|
||||
}
|
||||
|
||||
/* Consume the string constant, now that we don't need it anymore.
|
||||
** This should never fail since we checked the token type above.
|
||||
*/
|
||||
if (!Consume (TOK_SCONST, "String literal expected")) {
|
||||
return;
|
||||
/* Consume the string constant, now that we don't need it anymore.
|
||||
** This should never fail since we checked the token type above.
|
||||
*/
|
||||
Consume (TOK_SCONST, "String literal expected");
|
||||
}
|
||||
} else {
|
||||
/* No message. */
|
||||
|
@ -105,7 +105,24 @@ void ParseStaticAssert ()
|
|||
}
|
||||
}
|
||||
|
||||
/* Closing paren and semi-colon needed */
|
||||
ConsumeRParen ();
|
||||
ConsumeSemi ();
|
||||
/* The assertion failure error is not a syntax error */
|
||||
if (failed) {
|
||||
++PrevErrorCount;
|
||||
}
|
||||
|
||||
if (PrevErrorCount == ErrorCount) {
|
||||
/* Closing paren needed */
|
||||
ConsumeRParen ();
|
||||
}
|
||||
|
||||
if (PrevErrorCount == ErrorCount) {
|
||||
/* Must be followed by a semicolon */
|
||||
ConsumeSemi ();
|
||||
}
|
||||
|
||||
ExitPoint:
|
||||
/* Try some smart error recovery */
|
||||
if (PrevErrorCount != ErrorCount) {
|
||||
SmartErrorSkip (1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ static void CheckSymTable (SymTable* Tab)
|
|||
if (IS_Get (&WarnUnusedFunc)) {
|
||||
Warning ("Function '%s' is defined but never used", Entry->Name);
|
||||
}
|
||||
} else {
|
||||
} else if (!IsAnonName (Entry->Name)) {
|
||||
if (IS_Get (&WarnUnusedVar)) {
|
||||
Warning ("Variable '%s' is defined but never used", Entry->Name);
|
||||
}
|
||||
|
@ -919,14 +919,8 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl
|
|||
/* SCType must be struct or union */
|
||||
PRECONDITION (SCType == SC_STRUCT || SCType == SC_UNION);
|
||||
|
||||
if ((Flags & SC_FICTITIOUS) == 0) {
|
||||
/* Do we have an entry with this name already? */
|
||||
TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name));
|
||||
} else {
|
||||
/* Add a fictitious symbol in the fail-safe table */
|
||||
TagEntry = 0;
|
||||
CurTagTab = FailSafeTab;
|
||||
}
|
||||
/* Do we have an entry with this name already? */
|
||||
TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name));
|
||||
|
||||
if (TagEntry) {
|
||||
|
||||
|
@ -954,6 +948,15 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl
|
|||
if (DSFlags != 0) {
|
||||
*DSFlags |= DS_NEW_TYPE_DEF;
|
||||
}
|
||||
|
||||
if ((Flags & SC_FICTITIOUS) == SC_FICTITIOUS) {
|
||||
/* Add a fictitious symbol in the fail-safe table */
|
||||
TagEntry = 0;
|
||||
} else if (Size == 0) {
|
||||
/* Empty struct is not supported now */
|
||||
Error ("Empty %s type '%s' is not supported", SCType == SC_STRUCT ? "struct" : "union", Name);
|
||||
TagEntry = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2)
|
|||
/* Get the symbol types */
|
||||
const Type* Type1 = Sym1->Type;
|
||||
const Type* Type2 = Sym2->Type;
|
||||
typecmp_t CmpResult;
|
||||
|
||||
/* If either of both functions is old style, apply the default
|
||||
** promotions to the parameter type.
|
||||
|
@ -84,9 +85,10 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2)
|
|||
}
|
||||
}
|
||||
|
||||
/* Compare this field */
|
||||
if (TypeCmp (Type1, Type2).C < TC_EQUAL) {
|
||||
/* Field types not equal */
|
||||
/* Compare types of this parameter */
|
||||
CmpResult = TypeCmp (Type1, Type2);
|
||||
if (CmpResult.C < TC_EQUAL || (CmpResult.F & TCF_MASK_PARAM_DIFF) != 0) {
|
||||
/* The types are not compatible */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,15 +68,17 @@ typedef enum {
|
|||
TCF_VOID_PTR_ON_LEFT = 0x01, /* lhs is a void pointer */
|
||||
TCF_VOID_PTR_ON_RIGHT = 0x02, /* rhs is a void pointer */
|
||||
TCF_MASK_VOID_PTR = TCF_VOID_PTR_ON_LEFT | TCF_VOID_PTR_ON_RIGHT,
|
||||
TCF_QUAL_DIFF = 0x04, /* CVR qualifiers differ in a way that doesn't matter */
|
||||
TCF_QUAL_DIFF = 0x04, /* lhs doesn't have all of CVR qualifiers of rhs */
|
||||
TCF_QUAL_IMPLICIT = 0x08, /* CVR qualifiers of lhs are stricter than those of rhs */
|
||||
TCF_PTR_QUAL_DIFF = 0x10, /* CVR qualifiers of pointers differ */
|
||||
TCF_PTR_QUAL_IMPLICIT = 0x20, /* CVR qualifiers of pointers are stricter on lhs than those on rhs */
|
||||
TCF_MASK_C_QUAL_DIFF = 0x3C, /* All C Standard qualifiers */
|
||||
TCF_MASK_CVR_DIFF = 0x0C, /* All CVR qualifiers */
|
||||
TCF_PTR_QUAL_DIFF = 0x10, /* lhs pointee doesn't have all of CVR qualifiers of rhs pointee */
|
||||
TCF_PTR_QUAL_IMPLICIT = 0x20, /* CVR qualifiers of pointees are stricter on lhs than those on rhs */
|
||||
TCF_MASK_PTR_QUAL_DIFF = 0x30, /* All CVR qualifiers of pointees */
|
||||
TCF_ADDRSIZE_QUAL_DIFF = 0x40, /* Address size qualifiers differ */
|
||||
TCF_CCONV_QUAL_DIFF = 0x80, /* Function calling conventions differ. Unused now */
|
||||
TCF_INCOMPATIBLE_QUAL = TCF_ADDRSIZE_QUAL_DIFF | TCF_CCONV_QUAL_DIFF,
|
||||
TCF_MASK_QUAL = TCF_MASK_C_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL,
|
||||
TCF_MASK_PARAM_DIFF = TCF_MASK_PTR_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL,
|
||||
TCF_MASK_QUAL = TCF_MASK_CVR_DIFF | TCF_MASK_PTR_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL,
|
||||
} typecmpflag_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -321,15 +321,9 @@ void TypeCast (ExprDesc* Expr)
|
|||
{
|
||||
Type NewType[MAXTYPELEN];
|
||||
|
||||
/* Skip the left paren */
|
||||
NextToken ();
|
||||
|
||||
/* Read the type */
|
||||
/* Read the type enclosed in parentheses */
|
||||
ParseType (NewType);
|
||||
|
||||
/* Closing paren */
|
||||
ConsumeRParen ();
|
||||
|
||||
/* Read the expression we have to cast */
|
||||
hie10 (Expr);
|
||||
|
||||
|
@ -485,13 +479,6 @@ void TypeComposition (Type* lhs, const Type* rhs)
|
|||
} else if (RightCount != UNSPECIFIED) {
|
||||
SetElementCount (lhs, RightCount);
|
||||
}
|
||||
} else {
|
||||
/* Combine the qualifiers */
|
||||
if (IsClassPtr (lhs)) {
|
||||
++lhs;
|
||||
++rhs;
|
||||
lhs->C |= GetQualifier (rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Next type string element */
|
||||
|
|
5
test/err/bug2285-composite-type.c
Normal file
5
test/err/bug2285-composite-type.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
/* Bug #2285 - Regression in type composition */
|
||||
|
||||
void foo(); /* OK */
|
||||
void foo(int (*)(int)); /* OK */
|
||||
void foo(int (*)(long)); /* WRONG: Should be an error */
|
4
test/err/bug2286-param-qualifier.c
Normal file
4
test/err/bug2286-param-qualifier.c
Normal file
|
@ -0,0 +1,4 @@
|
|||
/* Bug #2286 - Qualifiers of pointees of function parameters ignored for type compatibility check */
|
||||
|
||||
void woo(int* p);
|
||||
void woo(const int* p); /* WRONG: Should be an error */
|
25
test/err/type-name-extra-identifier.c
Normal file
25
test/err/type-name-extra-identifier.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright 2023 The cc65 Authors
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
Test of type name with extra identifier
|
||||
*/
|
||||
|
||||
int a = sizeof (int b);
|
|
@ -58,24 +58,6 @@ $(ISEQUAL): ../isequal.c | $(WORKDIR)
|
|||
|
||||
define PRG_template
|
||||
|
||||
# should compile, but gives an error
|
||||
$(WORKDIR)/int-static-1888.$1.$2.prg: int-static-1888.c | $(WORKDIR)
|
||||
@echo "FIXME: " $$@ "currently does not compile."
|
||||
$(if $(QUIET),echo misc/int-static-1888.$1.$2.prg)
|
||||
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
|
||||
|
||||
# should compile, but gives an error
|
||||
$(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR)
|
||||
@echo "FIXME: " $$@ "currently does not compile."
|
||||
$(if $(QUIET),echo misc/bug760.$1.$2.prg)
|
||||
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
|
||||
|
||||
# should compile, but gives an error
|
||||
$(WORKDIR)/bug1437.$1.$2.prg: bug1437.c | $(WORKDIR)
|
||||
@echo "FIXME: " $$@ "currently does not compile."
|
||||
$(if $(QUIET),echo misc/bug1437.$1.$2.prg)
|
||||
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
|
||||
|
||||
# should compile, but gives an error
|
||||
$(WORKDIR)/bug1209-ind-goto-rev.$1.$2.prg: bug1209-ind-goto-rev.c | $(WORKDIR)
|
||||
@echo "FIXME: " $$@ "currently does not compile."
|
||||
|
@ -106,18 +88,6 @@ $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR)
|
|||
$(if $(QUIET),echo misc/pptest2.$1.$2.prg)
|
||||
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
|
||||
|
||||
# should compile, but gives an error
|
||||
$(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR)
|
||||
@echo "FIXME: " $$@ "currently does not compile."
|
||||
$(if $(QUIET),echo misc/bug1263.$1.$2.prg)
|
||||
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
|
||||
|
||||
# should compile, but gives an error
|
||||
$(WORKDIR)/bug1357.$1.$2.prg: bug1357.c | $(WORKDIR)
|
||||
@echo "FIXME: " $$@ "currently does not compile."
|
||||
$(if $(QUIET),echo misc/bug1357.$1.$2.prg)
|
||||
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
|
||||
|
||||
# should compile, but compiler exits with internal error
|
||||
$(WORKDIR)/bug1211-ice-move-refs-2.$1.$2.prg: bug1211-ice-move-refs-2.c | $(WORKDIR)
|
||||
@echo "FIXME: " $$@ "currently does not compile."
|
||||
|
|
|
@ -39,8 +39,18 @@ OPTIONS = g O Os Osi Osir Osr Oi Oir Or
|
|||
|
||||
ISEQUAL = ..$S..$Stestwrk$Sisequal$(EXE)
|
||||
|
||||
# NOTE: the current test bench may include K&R style C, C89 style C, C99 - and
|
||||
# even things from later standards. Technically C99 removed certain C89
|
||||
# constructs - However, so far GCC would still compile them and issue a
|
||||
# warning (instead of an error). Now, GCC 14 will be more strict about this,
|
||||
# and by default make those things an error instead. We use -std=gnu17 here
|
||||
# so we can still build the references with a modern compiler, and don't
|
||||
# have to deal with special-casing individual tests that use constructs
|
||||
# from those old standards. Should this become a problem in the future, we
|
||||
# will have to change that, and create said special cases here.
|
||||
# see discussion in https://github.com/cc65/cc65/issues/2277
|
||||
CC = gcc
|
||||
CFLAGS = -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow
|
||||
CFLAGS = -std=gnu17 -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
bug1889-missing-identifier.c:3: Error: Identifier expected
|
||||
bug1889-missing-identifier.c:3: Error: ';' expected
|
||||
bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers
|
||||
bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature
|
||||
bug1889-missing-identifier.c:4: Error: Identifier expected
|
||||
|
|
53
test/val/optimizer-bug-pr2262.c
Normal file
53
test/val/optimizer-bug-pr2262.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
|
||||
// optimizer bug that occured after PR #2262, fixed by PR #2295
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
unsigned char n;
|
||||
unsigned long fp1;
|
||||
|
||||
int failures = 0;
|
||||
|
||||
void test1(void)
|
||||
{
|
||||
asm("lda _n");
|
||||
asm("jeq %g", L0004);
|
||||
|
||||
asm("lda #$3F");
|
||||
asm("sta sreg+1");
|
||||
asm("lda #$C0");
|
||||
asm("sta sreg");
|
||||
asm("lda #$00");
|
||||
asm("ldx #$00");
|
||||
asm("jmp %g", L0005);
|
||||
|
||||
L0004:
|
||||
asm("lda #$3F");
|
||||
asm("sta sreg+1");
|
||||
asm("lda #$00");
|
||||
asm("sta sreg");
|
||||
asm("lda #$00");
|
||||
asm("ldx #$00");
|
||||
|
||||
L0005:
|
||||
asm("sta _fp1");
|
||||
asm("stx _fp1+1");
|
||||
asm("ldy sreg");
|
||||
asm("sty _fp1+2");
|
||||
asm("ldy sreg+1");
|
||||
asm("sty _fp1+3");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
n = 0;
|
||||
test1();
|
||||
printf("fp1:%08lx\n", fp1);
|
||||
if (fp1 != 0x3f000000) failures++;
|
||||
n = 0xff;
|
||||
test1();
|
||||
printf("fp1:%08lx\n", fp1);
|
||||
if (fp1 != 0x3fc00000) failures++;
|
||||
printf("failures:%d\n", failures);
|
||||
return failures;
|
||||
}
|
|
@ -168,6 +168,31 @@ void post_dec_assign_test(void)
|
|||
failures++;
|
||||
}
|
||||
|
||||
void dex_tests(void) {
|
||||
static unsigned int a, b;
|
||||
|
||||
a = 257;
|
||||
b = a - 1;
|
||||
if (b != 256) {
|
||||
printf("fail 257 => 256\n");
|
||||
failures++;
|
||||
}
|
||||
|
||||
a = 256;
|
||||
b = a - 1;
|
||||
if (b != 255) {
|
||||
printf("fail 256 => 255\n");
|
||||
failures++;
|
||||
}
|
||||
|
||||
a = 255;
|
||||
b = a - 1;
|
||||
if (b != 254) {
|
||||
printf("fail 255 => 254\n");
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int0 = 5;
|
||||
|
@ -186,6 +211,8 @@ int main(void)
|
|||
int1 = 5;
|
||||
post_dec_assign_test();
|
||||
|
||||
dex_tests();
|
||||
|
||||
printf("failures: %d\n",failures);
|
||||
|
||||
return failures;
|
||||
|
|
Loading…
Reference in New Issue
Block a user