diff --git a/.github/checks/Makefile b/.github/checks/Makefile
index 18cc153d4..93eeddd19 100644
--- a/.github/checks/Makefile
+++ b/.github/checks/Makefile
@@ -1,7 +1,23 @@
-.PHONY: checkstyle tabs lastline spaces noexec
+ifneq ($(shell echo),)
+ CMD_EXE = 1
+endif
-checkstyle: tabs lastline spaces noexec
+ifdef CMD_EXE
+
+.PHONY: checkstyle
+
+checkstyle:
+ $(info INFO: style checks require bash.)
+
+else
+
+.PHONY: checkstyle lineendings tabs lastline spaces noexec
+
+checkstyle: lineendings tabs lastline spaces noexec
+
+lineendings: lineendings.sh
+ @./lineendings.sh
tabs: tabs.sh
@./tabs.sh
@@ -14,3 +30,5 @@ spaces: spaces.sh
noexec: noexec.sh
@./noexec.sh
+
+endif
diff --git a/.github/checks/lineendings.sh b/.github/checks/lineendings.sh
new file mode 100755
index 000000000..5b445522f
--- /dev/null
+++ b/.github/checks/lineendings.sh
@@ -0,0 +1,18 @@
+#! /bin/bash
+OLDCWD=`pwd`
+SCRIPT_PATH=`dirname $0`
+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 -IUl $'\r'`
+
+cd $OLDCWD
+
+if [ x"$FILES"x != xx ]; then
+ echo "error: found CR in the following files:" >&2
+ for n in $FILES; do
+ echo $n >&2
+ done
+ exit -1
+fi
diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml
index 0ba0c6a1f..05d6a4a39 100644
--- a/.github/workflows/build-on-pull-request.yml
+++ b/.github/workflows/build-on-pull-request.yml
@@ -19,7 +19,7 @@ jobs:
- shell: bash
run: git config --global core.autocrlf input
- name: Checkout Source
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Do some simple style checks
shell: bash
@@ -57,7 +57,7 @@ jobs:
run: git config --global core.autocrlf input
- name: Checkout Source
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.1
diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml
index 769d778d5..e8be4400e 100644
--- a/.github/workflows/snapshot-on-push-master.yml
+++ b/.github/workflows/snapshot-on-push-master.yml
@@ -18,7 +18,7 @@ jobs:
run: git config --global core.autocrlf input
- name: Checkout Source
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.1
@@ -44,7 +44,7 @@ jobs:
- shell: bash
run: git config --global core.autocrlf input
- name: Checkout Source
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Do some simple style checks
shell: bash
@@ -97,7 +97,7 @@ jobs:
path: cc65-snapshot-win64.zip
- name: Get the online documents repo.
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
repository: cc65/doc
path: doc.git
diff --git a/Contributing.md b/Contributing.md
index ab1e73508..e316b9c61 100644
--- a/Contributing.md
+++ b/Contributing.md
@@ -8,11 +8,16 @@ This document contains all kinds of information that you should know if you want
* You must obey these rules when contributing new code or documentation to cc65. We are well aware that not all existing code may respect all rules outlined here - but this is no reason for you not to respect them.
* One commit/patch/PR per issue. Do not mix several things unless they are very closely related.
+* Sometimes when you make a PR, it may break completely unrelated tests. However, any PR is expected to merge cleanly with no failures. That means in practise that you are expected to fix/update the failing tests if required - for example this might be needed if you make changes to the compiler that changes the format of error- or warning messages. In that case you might have to update some reference files in the testbench. Obviously still check if that is actually the right thing to do ;)
# Codestyle rules
## All Sources
+### Line endings
+
+All files must only contain Unix style 'LF' line endings. Please configure your editors accordingly.
+
### TABs and spaces
This is an ongoing controversial topic - everyone knows that. However, the following is how we do it :)
diff --git a/doc/da65.sgml b/doc/da65.sgml
index bf074a667..185dbe0e6 100644
--- a/doc/da65.sgml
+++ b/doc/da65.sgml
@@ -461,7 +461,8 @@ following attributes are recognized:
END
This gives the end address of the range. The end address is inclusive, that
means, it is part of the range. Of course, it may not be smaller than the
- start address.
+ start address. Optionally, the end may be given as a decimal offset instead
+ of an absolute address, "+3", to specify it as a size.
NAME
This is a convenience attribute. It takes a string argument and will cause
diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s
index a32110ef2..e0cd94597 100644
--- a/libsrc/apple2/ser/a2.ssc.s
+++ b/libsrc/apple2/ser/a2.ssc.s
@@ -57,7 +57,9 @@
;----------------------------------------------------------------------------
; I/O definitions
-ACIA = $C088
+Offset = $8F ; Move 6502 false read out of I/O to page $BF
+
+ACIA = $C088-Offset
ACIA_DATA = ACIA+0 ; Data register
ACIA_STATUS = ACIA+1 ; Status register
ACIA_CMD = ACIA+2 ; Command register
@@ -197,6 +199,7 @@ SER_OPEN:
asl
asl
asl
+ adc Offset ; Assume carry to be clear
tax
; Check if the handshake setting is valid
diff --git a/libsrc/tgi/tgi_unload.s b/libsrc/tgi/tgi_unload.s
index 1012969d2..c01228080 100644
--- a/libsrc/tgi/tgi_unload.s
+++ b/libsrc/tgi/tgi_unload.s
@@ -29,6 +29,6 @@ _tgi_unload:
jmp _mod_free ; Free the driver
no_driver:
- lda #AddrModeBit & (AM65_ABS | AM65_ABS_X | AM65_ABS_Y))) {
+ if (CPU == CPU_65816 && (A->AddrModeBit & (AM65_ABS | AM65_ABS_X | AM65_ABS_Y | AM65_ABS_X_IND))) {
/* This is a 16 bit mode that uses an address. If in 65816,
** mode, force this address into 16 bit range to allow
** addressing inside a 64K segment.
diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c
index 9834ae5d1..54ab28d4e 100644
--- a/src/cc65/assignment.c
+++ b/src/cc65/assignment.c
@@ -315,12 +315,41 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op
} else if (Gen->Func == g_sub) {
g_dec (Flags | CF_CONST, Expr2.IVal);
} else {
- if (Expr2.IVal == 0) {
- /* Check for div by zero/mod by zero */
- if (Gen->Func == g_div) {
- Error ("Division by zero");
- } else if (Gen->Func == g_mod) {
- Error ("Modulo operation with zero");
+ if (!ED_IsUneval (Expr)) {
+ if (Expr2.IVal == 0) {
+ /* Check for div by zero/mod by zero */
+ if (Gen->Func == g_div) {
+ Warning ("Division by zero");
+ } else if (Gen->Func == g_mod) {
+ Warning ("Modulo operation with zero");
+ }
+ } else if (Gen->Func == g_asl || Gen->Func == g_asr) {
+ const Type* CalType = IntPromotion (Expr->Type);
+ unsigned ExprBits = BitSizeOf (CalType);
+
+ /* If the shift count is greater than or equal to the width of the
+ ** promoted left operand, the behaviour is undefined according to
+ ** the standard.
+ */
+ if (Expr2.IVal < 0) {
+ Warning ("Negative shift count %ld treated as %u for %s",
+ Expr2.IVal,
+ (unsigned)Expr2.IVal & (ExprBits - 1),
+ GetBasicTypeName (CalType));
+ } else if (Expr2.IVal >= (long)ExprBits) {
+ Warning ("Shift count %ld >= width of %s treated as %u",
+ Expr2.IVal,
+ GetBasicTypeName (CalType),
+ (unsigned)Expr2.IVal & (ExprBits - 1));
+ }
+
+ /* Here we simply "wrap" the shift count around the width */
+ Expr2.IVal &= ExprBits - 1;
+
+ /* Additional check for bit-fields */
+ if (Expr2.IVal >= (long)Expr->Type->A.B.Width) {
+ Warning ("Shift count %ld >= width of bit-field", Expr2.IVal);
+ }
}
}
@@ -495,12 +524,42 @@ static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char*
} else if (Gen->Func == g_sub) {
g_dec (Flags | CF_CONST, Expr2.IVal);
} else {
- if (Expr2.IVal == 0) {
- /* Check for div by zero/mod by zero */
- if (Gen->Func == g_div) {
- Error ("Division by zero");
- } else if (Gen->Func == g_mod) {
- Error ("Modulo operation with zero");
+ if (!ED_IsUneval (Expr)) {
+ if (Expr2.IVal == 0 && !ED_IsUneval (Expr)) {
+ /* Check for div by zero/mod by zero */
+ if (Gen->Func == g_div) {
+ Warning ("Division by zero");
+ } else if (Gen->Func == g_mod) {
+ Warning ("Modulo operation with zero");
+ }
+ } else if (Gen->Func == g_asl || Gen->Func == g_asr) {
+ const Type* CalType = IntPromotion (Expr->Type);
+ unsigned ExprBits = BitSizeOf (CalType);
+
+ /* If the shift count is greater than or equal to the width of the
+ ** promoted left operand, the behaviour is undefined according to
+ ** the standard.
+ */
+ if (Expr2.IVal < 0) {
+ Warning ("Negative shift count %ld treated as %u for %s",
+ Expr2.IVal,
+ (unsigned)Expr2.IVal & (ExprBits - 1),
+ GetBasicTypeName (CalType));
+ } else if (Expr2.IVal >= (long)ExprBits) {
+ Warning ("Shift count %ld >= width of %s treated as %u",
+ Expr2.IVal,
+ GetBasicTypeName (CalType),
+ (unsigned)Expr2.IVal & (ExprBits - 1));
+ }
+
+ /* Here we simply "wrap" the shift count around the width */
+ Expr2.IVal &= ExprBits - 1;
+
+ /* Additional check for bit width */
+ if (Expr2.IVal >= (long)BitSizeOf (Expr->Type)) {
+ Warning ("Shift count %ld >= width of %s",
+ Expr2.IVal, GetBasicTypeName (Expr->Type));
+ }
}
}
Gen->Func (Flags | CF_CONST, Expr2.IVal);
diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c
index e521fff73..251c973ee 100644
--- a/src/cc65/codegen.c
+++ b/src/cc65/codegen.c
@@ -41,6 +41,7 @@
/* common */
#include "addrsize.h"
+#include "attrib.h"
#include "check.h"
#include "cpu.h"
#include "shift.h"
@@ -64,7 +65,16 @@
#include "util.h"
#include "codegen.h"
-
+/* This is a terrible hack that tries to combat the ever reoccuring issue with
+ Mingw and PRIXPTR - the macro should have been defined like this for us in
+ the first place.
+ NOTE: "I64u" works in the github actions now, so if your local mingw64 fails,
+ you probably have to update.
+*/
+#if defined(__MINGW64__)
+#undef PRIXPTR
+#define PRIXPTR "I64u"
+#endif
/*****************************************************************************/
/* Helpers */
@@ -1282,34 +1292,49 @@ void g_tosint (unsigned flags)
-static void g_regchar (unsigned Flags)
-/* Make sure, the value in the primary register is in the range of char. Truncate if necessary */
+static void g_regchar (unsigned to)
+/* Treat the value in the primary register as a char with specified signedness
+** and convert it to an int (whose representation is irrelevent of signedness).
+*/
{
- unsigned L;
-
- AddCodeLine ("ldx #$00");
-
- if ((Flags & CF_UNSIGNED) == 0) {
- /* Sign extend */
- L = GetLocalLabel();
- AddCodeLine ("cmp #$80");
- AddCodeLine ("bcc %s", LocalLabelName (L));
- AddCodeLine ("dex");
- g_defcodelabel (L);
- }
+ /* Since char is the smallest type supported here, we never need any info
+ ** about the original type to "promote from it". However, we have to make
+ ** sure the entire AX contains the correct char value as an int, since we
+ ** will almost always use the char value as an int in AX directly in code
+ ** generation (unless CF_FORCECHAR is specified). That is to say, we don't
+ ** need the original "from" flags for the first conversion to char, but do
+ ** need the original "to" flags as the new "from" flags for the conversion
+ ** to int.
+ */
+ g_regint (to | CF_FORCECHAR);
}
-void g_regint (unsigned Flags)
-/* Make sure, the value in the primary register an int. Convert if necessary */
+void g_regint (unsigned from)
+/* Convert the value in the primary register to an int (whose representation
+** is irrelevent of signedness).
+*/
{
- switch (Flags & CF_TYPEMASK) {
+ switch (from & CF_TYPEMASK) {
case CF_CHAR:
- if (Flags & CF_FORCECHAR) {
- /* Conversion is from char */
- g_regchar (Flags);
+ /* If the original value was forced to use only A, it must be
+ ** extended from char to fill AX. Otherwise nothing to do here
+ ** since AX would already have the correct int value.
+ */
+ if (from & CF_FORCECHAR) {
+ AddCodeLine ("ldx #$00");
+
+ if ((from & CF_UNSIGNED) == 0) {
+ /* Sign extend */
+ unsigned L = GetLocalLabel();
+ AddCodeLine ("cmp #$80");
+ AddCodeLine ("bcc %s", LocalLabelName (L));
+ AddCodeLine ("dex");
+ g_defcodelabel (L);
+ }
+ break;
}
/* FALLTHROUGH */
@@ -1318,21 +1343,27 @@ void g_regint (unsigned Flags)
break;
default:
- typeerror (Flags);
+ typeerror (from);
}
}
-void g_reglong (unsigned Flags)
-/* Make sure, the value in the primary register a long. Convert if necessary */
+void g_reglong (unsigned from)
+/* Convert the value in the primary register to a long (whose representation
+** is irrelevent of signedness).
+*/
{
- switch (Flags & CF_TYPEMASK) {
+ switch (from & CF_TYPEMASK) {
case CF_CHAR:
- if (Flags & CF_FORCECHAR) {
+ /* If the original value was forced to use only A, it must be
+ ** extended from char to long. Otherwise AX would already have
+ ** the correct int value to be extened to long.
+ */
+ if (from & CF_FORCECHAR) {
/* Conversion is from char */
- if (Flags & CF_UNSIGNED) {
+ if (from & CF_UNSIGNED) {
if (IS_Get (&CodeSizeFactor) >= 200) {
AddCodeLine ("ldx #$00");
AddCodeLine ("stx sreg");
@@ -1342,18 +1373,19 @@ void g_reglong (unsigned Flags)
}
} else {
if (IS_Get (&CodeSizeFactor) >= 366) {
- g_regchar (Flags);
+ g_regint (from);
AddCodeLine ("stx sreg");
AddCodeLine ("stx sreg+1");
} else {
AddCodeLine ("jsr along");
}
}
+ break;
}
/* FALLTHROUGH */
case CF_INT:
- if (Flags & CF_UNSIGNED) {
+ if (from & CF_UNSIGNED) {
if (IS_Get (&CodeSizeFactor) >= 200) {
AddCodeLine ("ldy #$00");
AddCodeLine ("sty sreg");
@@ -1370,7 +1402,7 @@ void g_reglong (unsigned Flags)
break;
default:
- typeerror (Flags);
+ typeerror (from);
}
}
@@ -1507,48 +1539,49 @@ unsigned g_typeadjust (unsigned lhs, unsigned rhs)
-unsigned g_typecast (unsigned lhs, unsigned rhs)
-/* Cast the value in the primary register to the operand size that is flagged
-** by the lhs value. Return the result value.
+unsigned g_typecast (unsigned to, unsigned from)
+/* Cast the value in the primary register to the specified operand size and
+** signedness. Return the result flags.
*/
{
/* Check if a conversion is needed */
- if ((rhs & CF_CONST) == 0) {
- switch (lhs & CF_TYPEMASK) {
+ if ((from & CF_CONST) == 0) {
+ switch (to & CF_TYPEMASK) {
case CF_LONG:
- /* We must promote the primary register to long */
- g_reglong (rhs);
+ /* We must promote the primary register to long in EAX */
+ g_reglong (from);
break;
case CF_INT:
- /* We must promote the primary register to int */
- g_regint (rhs);
+ /* We must promote the primary register to int in AX */
+ g_regint (from);
break;
case CF_CHAR:
- /* We must truncate the primary register to char */
- g_regchar (lhs);
+ /* We must truncate the primary register to char and then
+ ** sign-extend it to signed int in AX.
+ */
+ g_regchar (to);
break;
default:
- typeerror (lhs);
+ /* Since we are switching on "to", report an error on it */
+ typeerror (to);
}
}
- /* Do not need any other action. If the left type is int, and the primary
+ /* Do not need any other action. If the "to" type is int, and the primary
** register is long, it will be automagically truncated. If the right hand
** side is const, it is not located in the primary register and handled by
** the expression parser code.
*/
/* Result is const if the right hand side was const */
- lhs |= (rhs & CF_CONST);
+ to |= (from & CF_CONST);
- /* The resulting type is that of the left hand side (that's why you called
- ** this function :-)
- */
- return lhs;
+ /* The resulting type is "to" (that's why you called this function :-) */
+ return to;
}
@@ -4561,7 +4594,7 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size)
-void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth)
+void g_testbitfield (ATTR_UNUSED(unsigned Flags), unsigned BitOffs, unsigned BitWidth)
/* Test bit-field in primary. */
{
/* Since the end is inclusive and cannot be negative here, we subtract 1 from the sum */
diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h
index cb62d78bd..8e04b45e4 100644
--- a/src/cc65/codegen.h
+++ b/src/cc65/codegen.h
@@ -208,11 +208,15 @@ void g_toslong (unsigned flags);
void g_tosint (unsigned flags);
/* Make sure, the value on TOS is an int. Convert if necessary */
-void g_regint (unsigned Flags);
-/* Make sure, the value in the primary register an int. Convert if necessary */
+void g_regint (unsigned from);
+/* Convert the value in the primary register to an int (whose representation
+** is irrelevent of signedness).
+*/
-void g_reglong (unsigned Flags);
-/* Make sure, the value in the primary register a long. Convert if necessary */
+void g_reglong (unsigned from);
+/* Convert the value in the primary register to a long (whose representation
+** is irrelevent of signedness).
+*/
unsigned g_typeadjust (unsigned lhs, unsigned rhs);
/* Adjust the integer operands before doing a binary operation. lhs is a flags
@@ -220,9 +224,9 @@ unsigned g_typeadjust (unsigned lhs, unsigned rhs);
** in (e)ax. The return value is the flags value for the resulting type.
*/
-unsigned g_typecast (unsigned lhs, unsigned rhs);
-/* Cast the value in the primary register to the operand size that is flagged
-** by the lhs value. Return the result value.
+unsigned g_typecast (unsigned to, unsigned from);
+/* Cast the value in the primary register to the specified operand size and
+** signedness. Return the result flags.
*/
void g_scale (unsigned flags, long val);
diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c
index 5949c0c6f..e621147ab 100644
--- a/src/cc65/codeseg.c
+++ b/src/cc65/codeseg.c
@@ -399,10 +399,7 @@ static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L)
default:
/* Absolute, maybe indexed */
- L = ReadToken (L, ", ", Arg, sizeof (Arg));
- if (*L == ' ') {
- L = SkipSpace (L+1);
- }
+ L = ReadToken (L, ",", Arg, sizeof (Arg));
if (*L == '\0') {
/* Absolute, zeropage or branch */
if ((OPC->Info & OF_BRA) != 0) {
diff --git a/src/cc65/compile.c b/src/cc65/compile.c
index 11dcbced0..73380f3df 100644
--- a/src/cc65/compile.c
+++ b/src/cc65/compile.c
@@ -79,7 +79,7 @@ static void Parse (void)
/* Top level parser routine. */
{
int comma;
- SymEntry* Entry;
+ SymEntry* Sym;
FuncDesc* FuncDef = 0;
/* Initialization for deferred operations */
@@ -123,7 +123,7 @@ static void Parse (void)
}
/* Read variable defs and functions */
- ParseDeclSpec (&Spec, SC_EXTERN | SC_STATIC, T_INT);
+ ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_EXTERN | SC_STATIC);
/* Don't accept illegal storage classes */
if ((Spec.StorageClass & SC_TYPEMASK) == 0) {
@@ -142,11 +142,11 @@ static void Parse (void)
}
/* Read declarations for this type */
- Entry = 0;
+ Sym = 0;
comma = 0;
while (1) {
- Declaration Decl;
+ Declarator Decl;
/* Read the next declaration */
ParseDecl (&Spec, &Decl, DM_NEED_IDENT);
@@ -196,10 +196,10 @@ static void Parse (void)
}
/* Add an entry to the symbol table */
- Entry = AddGlobalSym (Decl.Ident, Decl.Type, Decl.StorageClass);
+ Sym = AddGlobalSym (Decl.Ident, Decl.Type, Decl.StorageClass);
/* Add declaration attributes */
- SymUseAttr (Entry, &Decl);
+ SymUseAttr (Sym, &Decl);
/* Reserve storage for the variable if we need to */
if (Decl.StorageClass & SC_STORAGE) {
@@ -211,11 +211,11 @@ static void Parse (void)
if (CurTok.Tok == TOK_ASSIGN) {
/* This is a definition with storage */
- if (SymIsDef (Entry)) {
+ if (SymIsDef (Sym)) {
Error ("Global variable '%s' has already been defined",
- Entry->Name);
+ Sym->Name);
}
- Entry->Flags |= SC_DEF;
+ Sym->Flags |= SC_DEF;
/* We cannot initialize types of unknown size, or
** void types in ISO modes.
@@ -245,21 +245,21 @@ static void Parse (void)
}
/* Define a label */
- g_defgloblabel (Entry->Name);
+ g_defgloblabel (Sym->Name);
/* Skip the '=' */
NextToken ();
/* Parse the initialization */
- ParseInit (Entry->Type);
+ ParseInit (Sym->Type);
} else {
/* This is a declaration */
if (IsTypeVoid (Decl.Type)) {
/* We cannot declare variables of type void */
Error ("Illegal type for variable '%s'", Decl.Ident);
- Entry->Flags &= ~(SC_STORAGE | SC_DEF);
- } else if (Size == 0 && SymIsDef (Entry) && !IsEmptiableObjectType (Decl.Type)) {
+ Sym->Flags &= ~(SC_STORAGE | SC_DEF);
+ } else if (Size == 0 && SymIsDef (Sym) && !IsEmptiableObjectType (Decl.Type)) {
/* Size is unknown. Is it an array? */
if (!IsTypeArray (Decl.Type)) {
Error ("Variable '%s' has unknown size", Decl.Ident);
@@ -286,11 +286,11 @@ static void Parse (void)
*/
const char* bssName = GetSegName (SEG_BSS);
- if (Entry->V.BssName && strcmp (Entry->V.BssName, bssName) != 0) {
+ if (Sym->V.BssName && strcmp (Sym->V.BssName, bssName) != 0) {
Error ("Global variable '%s' already was defined in the '%s' segment.",
- Entry->Name, Entry->V.BssName);
+ Sym->Name, Sym->V.BssName);
}
- Entry->V.BssName = xstrdup (bssName);
+ Sym->V.BssName = xstrdup (bssName);
/* This is to make the automatical zeropage setting of the symbol
** work right.
@@ -300,9 +300,9 @@ static void Parse (void)
}
/* Make the symbol zeropage according to the segment address size */
- if ((Entry->Flags & SC_STATIC) != 0) {
+ if ((Sym->Flags & SC_STATIC) != 0) {
if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) {
- Entry->Flags |= SC_ZEROPAGE;
+ Sym->Flags |= SC_ZEROPAGE;
}
}
@@ -318,7 +318,7 @@ static void Parse (void)
}
/* Function declaration? */
- if (Entry && IsTypeFunc (Entry->Type)) {
+ if (Sym && IsTypeFunc (Sym->Type)) {
/* Function */
if (!comma) {
@@ -327,7 +327,7 @@ static void Parse (void)
NextToken ();
} else {
/* Parse the function body */
- NewFunc (Entry, FuncDef);
+ NewFunc (Sym, FuncDef);
/* Make sure we aren't omitting any work */
CheckDeferredOpAllDone ();
@@ -478,8 +478,9 @@ void Compile (const char* FileName)
for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) {
if ((Entry->Flags & (SC_STORAGE | SC_DEF | SC_STATIC)) == (SC_STORAGE | SC_STATIC)) {
/* Assembly definition of uninitialized global variable */
- SymEntry* Sym = GetSymType (Entry->Type);
+ SymEntry* TagSym = GetESUTagSym (Entry->Type);
unsigned Size = SizeOf (Entry->Type);
+
if (Size == 0 && IsTypeArray (Entry->Type)) {
if (GetElementCount (Entry->Type) == UNSPECIFIED) {
/* Assume array size of 1 */
@@ -488,11 +489,11 @@ void Compile (const char* FileName)
Warning ("Incomplete array '%s[]' assumed to have one element", Entry->Name);
}
- Sym = GetSymType (GetElementType (Entry->Type));
+ TagSym = GetESUTagSym (GetElementType (Entry->Type));
}
/* For non-ESU types, Size != 0 */
- if (Size != 0 || (Sym != 0 && SymIsDef (Sym))) {
+ if (Size != 0 || (TagSym != 0 && SymIsDef (TagSym))) {
/* Set the segment name only when it changes */
if (strcmp (GetSegName (SEG_BSS), Entry->V.BssName) != 0) {
SetSegName (SEG_BSS, Entry->V.BssName);
diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c
index 6d9afa403..023aefaf7 100644
--- a/src/cc65/datatype.c
+++ b/src/cc65/datatype.c
@@ -48,6 +48,7 @@
#include "fp.h"
#include "funcdesc.h"
#include "global.h"
+#include "ident.h"
#include "symtab.h"
@@ -86,297 +87,6 @@ const Type type_c_void_p[] = { TYPE(T_PTR), TYPE(T_C_VOID), TYPE(T_END) };
-static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBuf* East, const Type* T)
-/* Return the name string of the given type split into a western part and an
-** eastern part.
-*/
-{
- struct StrBuf Buf = AUTO_STRBUF_INITIALIZER;
-
- if (IsTypeArray (T)) {
-
- long Count = GetElementCount (T);
- if (!SB_IsEmpty (East)) {
- if (Count > 0) {
- SB_Printf (&Buf, "[%ld]", Count);
- } else {
- SB_Printf (&Buf, "[]");
- }
- SB_Append (East, &Buf);
- SB_Terminate (East);
-
- } else {
- if (Count > 0) {
- SB_Printf (East, "[%ld]", Count);
- } else {
- SB_Printf (East, "[]");
- }
-
- if (!SB_IsEmpty (West)) {
- /* Add parentheses to West */
- SB_Printf (&Buf, "(%s)", SB_GetConstBuf (West));
- SB_Copy (West, &Buf);
- SB_Terminate (West);
- }
- }
-
- /* Get element type */
- GetFullTypeNameWestEast (West, East, T + 1);
-
- } else if (IsTypeFunc (T)) {
-
- FuncDesc* D = GetFuncDesc (T);
- struct StrBuf ParamList = AUTO_STRBUF_INITIALIZER;
-
- /* First argument */
- SymEntry* Param = D->SymTab->SymHead;
- unsigned I;
- for (I = 0; I < D->ParamCount; ++I) {
- CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0);
- if (I > 0) {
- SB_AppendStr (&ParamList, ", ");
- }
- SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type)));
- SB_Clear (&Buf);
- /* Next argument */
- Param = Param->NextSym;
- }
- if ((D->Flags & FD_VARIADIC) == 0) {
- if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) {
- SB_AppendStr (&ParamList, "void");
- }
- } else {
- if (D->ParamCount > 0) {
- SB_AppendStr (&ParamList, ", ...");
- } else {
- SB_AppendStr (&ParamList, "...");
- }
- }
- SB_Terminate (&ParamList);
-
- /* Join the existing West and East together */
- if (!SB_IsEmpty (East)) {
- SB_Append (West, East);
- SB_Terminate (West);
- SB_Clear (East);
- }
-
- if (SB_IsEmpty (West)) {
- /* Just use the param list */
- SB_Printf (West, "(%s)", SB_GetConstBuf (&ParamList));
- } else {
- /* Append the param list to the existing West */
- SB_Printf (&Buf, "(%s)(%s)", SB_GetConstBuf (West), SB_GetConstBuf (&ParamList));
- SB_Printf (West, "%s", SB_GetConstBuf (&Buf));
- }
- SB_Done (&ParamList);
-
- /* Return type */
- GetFullTypeNameWestEast (West, East, T + 1);
-
- } else if (IsTypePtr (T)) {
-
- int QualCount = 0;
-
- SB_Printf (&Buf, "*");
-
- /* Add qualifiers */
- if ((GetQualifier (T) & ~T_QUAL_NEAR) != T_QUAL_NONE) {
- QualCount = GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR);
- }
-
- if (!SB_IsEmpty (West)) {
- if (QualCount > 0) {
- SB_AppendChar (&Buf, ' ');
- }
- SB_Append (&Buf, West);
- }
-
- SB_Copy (West, &Buf);
- SB_Terminate (West);
-
- /* Get indirection type */
- GetFullTypeNameWestEast (West, East, T + 1);
-
- } else {
-
- /* Add qualifiers */
- if ((GetQualifier (T) & ~T_QUAL_NEAR) != 0) {
- if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR) > 0) {
- SB_AppendChar (&Buf, ' ');
- }
- }
-
- if (!IsTypeBitField (T)) {
- SB_AppendStr (&Buf, GetSymTypeName (T));
- } else {
- SB_AppendStr (&Buf, GetBasicTypeName (T + 1));
- }
-
- if (!SB_IsEmpty (West)) {
- SB_AppendChar (&Buf, ' ');
- SB_Append (&Buf, West);
- }
-
- SB_Copy (West, &Buf);
- SB_Terminate (West);
- }
-
- SB_Done (&Buf);
- return West;
-}
-
-
-
-const char* GetBasicTypeName (const Type* T)
-/* Return a const name string of the basic type.
-** Return "type" for unknown basic types.
-*/
-{
- switch (GetRawType (T)) {
- case T_TYPE_ENUM: return "enum";
- case T_TYPE_BITFIELD: return "bit-field";
- case T_TYPE_FLOAT: return "float";
- case T_TYPE_DOUBLE: return "double";
- case T_TYPE_VOID: return "void";
- case T_TYPE_STRUCT: return "struct";
- case T_TYPE_UNION: return "union";
- case T_TYPE_ARRAY: return "array";
- case T_TYPE_PTR: return "pointer";
- case T_TYPE_FUNC: return "function";
- case T_TYPE_NONE: /* FALLTHROUGH */
- default: break;
- }
- if (IsClassInt (T)) {
- if (IsRawSignSigned (T)) {
- switch (GetRawType (T)) {
- case T_TYPE_CHAR: return "signed char";
- case T_TYPE_SHORT: return "short";
- case T_TYPE_INT: return "int";
- case T_TYPE_LONG: return "long";
- case T_TYPE_LONGLONG: return "long long";
- default:
- return "signed integer";
- }
- } else if (IsRawSignUnsigned (T)) {
- switch (GetRawType (T)) {
- case T_TYPE_CHAR: return "unsigned char";
- case T_TYPE_SHORT: return "unsigned short";
- case T_TYPE_INT: return "unsigned int";
- case T_TYPE_LONG: return "unsigned long";
- case T_TYPE_LONGLONG: return "unsigned long long";
- default:
- return "unsigned integer";
- }
- } else {
- switch (GetRawType (T)) {
- case T_TYPE_CHAR: return "char";
- case T_TYPE_SHORT: return "short";
- case T_TYPE_INT: return "int";
- case T_TYPE_LONG: return "long";
- case T_TYPE_LONGLONG: return "long long";
- default:
- return "integer";
- }
- }
- }
- return "type";
-}
-
-
-
-const char* GetFullTypeName (const Type* T)
-/* Return the full name string of the given type */
-{
- struct StrBuf* Buf = NewDiagnosticStrBuf ();
- GetFullTypeNameBuf (Buf, T);
-
- return SB_GetConstBuf (Buf);
-}
-
-
-
-struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T)
-/* Return the full name string of the given type */
-{
- struct StrBuf East = AUTO_STRBUF_INITIALIZER;
- GetFullTypeNameWestEast (S, &East, T);
-
- /* Join West and East */
- SB_Append (S, &East);
- SB_Terminate (S);
- SB_Done (&East);
-
- return S;
-}
-
-
-
-int GetQualifierTypeCodeNameBuf (struct StrBuf* S, TypeCode Qual, TypeCode IgnoredQual)
-/* Return the names of the qualifiers of the type.
-** Qualifiers to be ignored can be specified with the IgnoredQual flags.
-** Return the count of added qualifier names.
-*/
-{
- int Count = 0;
-
- Qual &= T_MASK_QUAL & ~IgnoredQual;
- if (Qual & T_QUAL_CONST) {
- if (!SB_IsEmpty (S)) {
- SB_AppendChar (S, ' ');
- }
- SB_AppendStr (S, "const");
- ++Count;
- }
- if (Qual & T_QUAL_VOLATILE) {
- if (Count > 0) {
- SB_AppendChar (S, ' ');
- }
- SB_AppendStr (S, "volatile");
- ++Count;
- }
- if (Qual & T_QUAL_RESTRICT) {
- if (Count > 0) {
- SB_AppendChar (S, ' ');
- }
- SB_AppendStr (S, "restrict");
- ++Count;
- }
- if (Qual & T_QUAL_NEAR) {
- if (Count > 0) {
- SB_AppendChar (S, ' ');
- }
- SB_AppendStr (S, "__near__");
- ++Count;
- }
- if (Qual & T_QUAL_FAR) {
- SB_AppendStr (S, "__far__");
- ++Count;
- }
- if (Qual & T_QUAL_FASTCALL) {
- if (Count > 0) {
- SB_AppendChar (S, ' ');
- }
- SB_AppendStr (S, "__fastcall__");
- ++Count;
- }
- if (Qual & T_QUAL_CDECL) {
- if (Count > 0) {
- SB_AppendChar (S, ' ');
- }
- SB_AppendStr (S, "__cdecl__");
- ++Count;
- }
-
- if (Count > 0) {
- SB_Terminate (S);
- }
-
- return Count;
-}
-
-
-
unsigned TypeLen (const Type* T)
/* Return the length of the type string */
{
@@ -433,6 +143,12 @@ void TypeFree (Type* T)
+/*****************************************************************************/
+/* Type info extraction */
+/*****************************************************************************/
+
+
+
int SignExtendChar (int C)
/* Do correct sign extension of a character */
{
@@ -445,70 +161,6 @@ int SignExtendChar (int C)
-Type* GetCharArrayType (unsigned Len)
-/* Return the type for a char array of the given length */
-{
- /* Allocate memory for the type string */
- Type* T = TypeAlloc (3); /* array/char/terminator */
-
- /* Fill the type string */
- T[0].C = T_ARRAY;
- T[0].A.L = Len; /* Array length is in the L attribute */
- T[1].C = T_CHAR;
- T[2].C = T_END;
-
- /* Return the new type */
- return T;
-}
-
-
-
-Type* GetImplicitFuncType (void)
-/* Return a type string for an inplicitly declared function */
-{
- /* Get a new function descriptor */
- FuncDesc* F = NewFuncDesc ();
-
- /* Allocate memory for the type string */
- Type* T = TypeAlloc (3); /* func/returns int/terminator */
-
- /* Prepare the function descriptor */
- F->Flags = FD_EMPTY;
- F->SymTab = &EmptySymTab;
- F->TagTab = &EmptySymTab;
-
- /* Fill the type string */
- T[0].C = T_FUNC | CodeAddrSizeQualifier ();
- T[0].A.F = F;
- T[1].C = T_INT;
- T[2].C = T_END;
-
- /* Return the new type */
- return T;
-}
-
-
-
-const Type* GetStructReplacementType (const Type* SType)
-/* Get a replacement type for passing a struct/union in the primary register */
-{
- const Type* NewType;
- /* If the size is less than or equal to that of a long, we will copy the
- ** struct using the primary register, otherwise we will use memcpy.
- */
- switch (SizeOf (SType)) {
- case 1: NewType = type_uchar; break;
- case 2: NewType = type_uint; break;
- case 3: /* FALLTHROUGH */
- case 4: NewType = type_ulong; break;
- default: NewType = SType; break;
- }
-
- return NewType;
-}
-
-
-
long GetIntegerTypeMin (const Type* Type)
/* Get the smallest possible value of the integer type.
** The type must have a known size.
@@ -551,169 +203,16 @@ unsigned long GetIntegerTypeMax (const Type* Type)
-static unsigned GetBitFieldMinimalTypeSize (unsigned BitWidth)
-/* Return the size of the smallest integer type that may have BitWidth bits */
+unsigned BitSizeOf (const Type* T)
+/* Return the size (in bit-width) of a data type */
{
- /* Since all integer types supported in cc65 for bit-fields have sizes that
- ** are powers of 2, we can just use this bit-twiddling trick.
- */
- unsigned V = (int)(BitWidth - 1U) / (int)CHAR_BITS;
- V |= V >> 1;
- V |= V >> 2;
- V |= V >> 4;
- V |= V >> 8;
- V |= V >> 16;
-
- /* Return the result size */
- return V + 1U;
-}
-
-
-static unsigned TypeOfBySize (unsigned Size)
-/* Get the code generator replacement type of the object by its size */
-{
- unsigned NewType;
- /* If the size is less than or equal to that of a a long, we will copy
- ** the struct using the primary register, otherwise we use memcpy.
- */
- switch (Size) {
- case 1: NewType = CF_CHAR; break;
- case 2: NewType = CF_INT; break;
- case 3: /* FALLTHROUGH */
- case 4: NewType = CF_LONG; break;
- default: NewType = CF_NONE; break;
- }
-
- return NewType;
-}
-
-
-
-const Type* GetUnderlyingType (const Type* Type)
-/* Get the underlying type of an enum or other integer class type */
-{
- if (IsISOChar (Type)) {
- return IS_Get (&SignedChars) ? type_schar : type_uchar;
- } else if (IsTypeEnum (Type)) {
- /* This should not happen, but just in case */
- if (Type->A.S == 0) {
- Internal ("Enum tag type error in GetUnderlyingTypeCode");
- }
-
- /* If incomplete enum type is used, just return its raw type */
- if (Type->A.S->V.E.Type != 0) {
- return Type->A.S->V.E.Type;
- }
- } else if (IsTypeBitField (Type)) {
- /* We consider the smallest type that can represent all values of the
- ** bit-field, instead of the type used in the declaration, the truly
- ** underlying of the bit-field.
- */
- switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
- case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break;
- case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break;
- case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break;
- default: Type = IsSignSigned (Type) ? type_int : type_uint; break;
- }
- }
-
- return Type;
-}
-
-
-
-TypeCode GetUnderlyingTypeCode (const Type* Type)
-/* Get the type code of the unqualified underlying type of TCode.
-** Return UnqualifiedType (TCode) if TCode is not scalar.
-*/
-{
- TypeCode Underlying = UnqualifiedType (Type->C);
-
- if (IsISOChar (Type)) {
-
- return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR;
-
- } else if (IsTypeEnum (Type)) {
- TypeCode TCode;
-
- /* This should not happen, but just in case */
- if (Type->A.S == 0) {
- Internal ("Enum tag type error in GetUnderlyingTypeCode");
- }
-
- /* Inspect the underlying type of the enum */
- if (Type->A.S->V.E.Type == 0) {
- /* Incomplete enum type is used */
- return Underlying;
- }
- TCode = UnqualifiedType (Type->A.S->V.E.Type->C);
-
- /* Replace the type code with integer */
- Underlying = (TCode & ~T_MASK_TYPE);
- switch (TCode & T_MASK_SIZE) {
- case T_SIZE_INT: Underlying |= T_TYPE_INT; break;
- case T_SIZE_LONG: Underlying |= T_TYPE_LONG; break;
- case T_SIZE_SHORT: Underlying |= T_TYPE_SHORT; break;
- case T_SIZE_CHAR: Underlying |= T_TYPE_CHAR; break;
- case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break;
- default: Underlying |= T_TYPE_INT; break;
- }
- } else if (IsTypeBitField (Type)) {
- /* We consider the smallest type that can represent all values of the
- ** bit-field, instead of the type used in the declaration, the truly
- ** underlying of the bit-field.
- */
- switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
- case SIZEOF_CHAR: Underlying = T_CHAR; break;
- case SIZEOF_INT: Underlying = T_INT; break;
- case SIZEOF_LONG: Underlying = T_LONG; break;
- case SIZEOF_LONGLONG: Underlying = T_LONGLONG; break;
- default: Underlying = T_INT; break;
- }
- Underlying &= ~T_MASK_SIGN;
- Underlying |= Type->C & T_MASK_SIGN;
- }
-
- return Underlying;
-}
-
-
-
-const Type* GetBitFieldChunkType (const Type* Type)
-/* Get the type needed to operate on the byte chunk containing the bit-field */
-{
- unsigned ChunkSize;
- if ((Type->A.B.Width - 1U) / CHAR_BITS ==
- (Type->A.B.Offs + Type->A.B.Width - 1U) / CHAR_BITS) {
- /* T bit-field fits within its underlying type */
- return GetUnderlyingType (Type);
- }
-
- ChunkSize = GetBitFieldMinimalTypeSize (Type->A.B.Offs + Type->A.B.Width);
- if (ChunkSize < SizeOf (Type + 1)) {
- /* The end of the bit-field is offset by some bits so that it requires
- ** more bytes to be accessed as a whole than its underlying type does.
- ** Note: In cc65 the bit offset is always less than CHAR_BITS.
- */
- switch (ChunkSize) {
- case SIZEOF_CHAR: return IsSignSigned (Type) ? type_schar : type_uchar;
- case SIZEOF_INT: return IsSignSigned (Type) ? type_int : type_uint;
- case SIZEOF_LONG: return IsSignSigned (Type) ? type_long : type_ulong;
- default: return IsSignSigned (Type) ? type_int : type_uint;
- }
- }
-
- /* We can always use the declarartion integer type as the chunk type.
- ** Note: A bit-field will not occupy bits located in bytes more than that
- ** of its declaration type in cc65. So this is OK.
- */
- return Type + 1;
+ return IsTypeBitField (T) ? T->A.B.Width : CHAR_BITS * SizeOf (T);
}
unsigned SizeOf (const Type* T)
-/* Compute size of object represented by type array. */
+/* Compute size (in bytes) of object represented by type array */
{
switch (GetUnderlyingTypeCode (T)) {
@@ -786,7 +285,7 @@ unsigned SizeOf (const Type* T)
unsigned PSizeOf (const Type* T)
-/* Compute size of pointer object. */
+/* Compute size (in bytes) of pointee object */
{
/* We are expecting a pointer expression */
CHECK (IsClassPtr (T));
@@ -797,10 +296,21 @@ unsigned PSizeOf (const Type* T)
+unsigned CheckedBitSizeOf (const Type* T)
+/* Return the size (in bit-width) of a data type. If the size is zero, emit an
+** error and return some valid size instead (so the rest of the compiler
+** doesn't have to work with invalid sizes).
+*/
+{
+ return IsTypeBitField (T) ? T->A.B.Width : CHAR_BITS * CheckedSizeOf (T);
+}
+
+
+
unsigned CheckedSizeOf (const Type* T)
-/* Return the size of a data type. If the size is zero, emit an error and
-** return some valid size instead (so the rest of the compiler doesn't have
-** to work with invalid sizes).
+/* Return the size (in bytes) of a data type. If the size is zero, emit an
+** error and return some valid size instead (so the rest of the compiler
+** doesn't have to work with invalid sizes).
*/
{
unsigned Size = SizeOf (T);
@@ -818,9 +328,9 @@ unsigned CheckedSizeOf (const Type* T)
unsigned CheckedPSizeOf (const Type* T)
-/* Return the size of a data type that is pointed to by a pointer. If the
-** size is zero, emit an error and return some valid size instead (so the
-** rest of the compiler doesn't have to work with invalid sizes).
+/* Return the size (in bytes) of a data type that is pointed to by a pointer.
+** If the size is zero, emit an error and return some valid size instead (so
+** the rest of the compiler doesn't have to work with invalid sizes).
*/
{
unsigned Size = PSizeOf (T);
@@ -837,76 +347,201 @@ unsigned CheckedPSizeOf (const Type* T)
-unsigned TypeOf (const Type* T)
-/* Get the code generator base type of the object */
+static unsigned GetBitFieldMinimalTypeSize (unsigned BitWidth)
+/* Return the size of the smallest integer type that may have BitWidth bits */
{
- unsigned NewType;
+ /* Since all integer types supported in cc65 for bit-fields have sizes that
+ ** are powers of 2, we can just use this bit-twiddling trick.
+ */
+ unsigned V = (int)(BitWidth - 1U) / (int)CHAR_BITS;
+ V |= V >> 1;
+ V |= V >> 2;
+ V |= V >> 4;
+ V |= V >> 8;
+ V |= V >> 16;
- switch (GetUnderlyingTypeCode (T)) {
-
- case T_SCHAR:
- return CF_CHAR;
-
- case T_UCHAR:
- return CF_CHAR | CF_UNSIGNED;
-
- case T_SHORT:
- case T_INT:
- return CF_INT;
-
- case T_USHORT:
- case T_UINT:
- case T_PTR:
- case T_ARRAY:
- return CF_INT | CF_UNSIGNED;
-
- case T_LONG:
- return CF_LONG;
-
- case T_ULONG:
- return CF_LONG | CF_UNSIGNED;
-
- case T_FLOAT:
- case T_DOUBLE:
- /* These two are identical in the backend */
- return CF_FLOAT;
-
- case T_FUNC:
- /* Treat this as a function pointer */
- return CF_INT | CF_UNSIGNED;
-
- case T_STRUCT:
- case T_UNION:
- NewType = TypeOfBySize (SizeOf (T));
- if (NewType != CF_NONE) {
- return NewType;
- }
- /* Address of ... */
- return CF_INT | CF_UNSIGNED;
-
- case T_VOID:
- case T_ENUM:
- /* Incomplete enum type */
- Error ("Incomplete type '%s'", GetFullTypeName (T));
- return CF_INT;
-
- default:
- Error ("Illegal type %04lX", T->C);
- return CF_INT;
- }
+ /* Return the result size */
+ return V + 1U;
}
-unsigned FuncTypeOf (const Type* T)
-/* Get the code generator flag for calling the function */
+TypeCode GetUnderlyingTypeCode (const Type* Type)
+/* Get the type code of the unqualified underlying type of TCode.
+** Return UnqualTypeCode (Type) if Type is not scalar.
+*/
{
- if (GetUnderlyingTypeCode (T) == T_FUNC) {
- return (T->A.F->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC;
- } else {
- Error ("Illegal function type %04lX", T->C);
- return 0;
+ TypeCode Underlying = UnqualifiedType (Type->C);
+
+ if (IsISOChar (Type)) {
+
+ return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR;
+
+ } else if (IsTypeEnum (Type)) {
+ TypeCode TCode;
+
+ /* This should not happen, but just in case */
+ if (Type->A.S == 0) {
+ Internal ("Enum tag type error in GetUnderlyingTypeCode");
+ }
+
+ /* Inspect the underlying type of the enum */
+ if (Type->A.S->V.E.Type == 0) {
+ /* Incomplete enum type is used */
+ return Underlying;
+ }
+ TCode = UnqualifiedType (Type->A.S->V.E.Type->C);
+
+ /* Replace the type code with integer */
+ Underlying = (TCode & ~T_MASK_TYPE);
+ switch (TCode & T_MASK_SIZE) {
+ case T_SIZE_INT: Underlying |= T_TYPE_INT; break;
+ case T_SIZE_LONG: Underlying |= T_TYPE_LONG; break;
+ case T_SIZE_SHORT: Underlying |= T_TYPE_SHORT; break;
+ case T_SIZE_CHAR: Underlying |= T_TYPE_CHAR; break;
+ case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break;
+ default: Underlying |= T_TYPE_INT; break;
+ }
+ } else if (IsTypeBitField (Type)) {
+ /* We consider the smallest type that can represent all values of the
+ ** bit-field, instead of the type used in the declaration, the truly
+ ** underlying of the bit-field.
+ */
+ switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
+ case SIZEOF_CHAR: Underlying = T_CHAR; break;
+ case SIZEOF_INT: Underlying = T_INT; break;
+ case SIZEOF_LONG: Underlying = T_LONG; break;
+ case SIZEOF_LONGLONG: Underlying = T_LONGLONG; break;
+ default: Underlying = T_INT; break;
+ }
+ Underlying &= ~T_MASK_SIGN;
+ Underlying |= Type->C & T_MASK_SIGN;
}
+
+ return Underlying;
+}
+
+
+
+/*****************************************************************************/
+/* Type manipulation */
+/*****************************************************************************/
+
+
+
+Type* GetImplicitFuncType (void)
+/* Return a type string for an implicitly declared function */
+{
+ /* Get a new function descriptor */
+ FuncDesc* F = NewFuncDesc ();
+
+ /* Allocate memory for the type string */
+ Type* T = TypeAlloc (3); /* func/returns int/terminator */
+
+ /* Prepare the function descriptor */
+ F->Flags = FD_EMPTY;
+ F->SymTab = &EmptySymTab;
+ F->TagTab = &EmptySymTab;
+
+ /* Fill the type string */
+ T[0].C = T_FUNC | CodeAddrSizeQualifier ();
+ T[0].A.F = F;
+ T[1].C = T_INT;
+ T[2].C = T_END;
+
+ /* Return the new type */
+ return T;
+}
+
+
+
+Type* GetCharArrayType (unsigned Len)
+/* Return the type for a char array of the given length */
+{
+ /* Allocate memory for the type string */
+ Type* T = TypeAlloc (3); /* array/char/terminator */
+
+ /* Fill the type string */
+ T[0].C = T_ARRAY;
+ T[0].A.L = Len; /* Array length is in the L attribute */
+ T[1].C = T_CHAR;
+ T[2].C = T_END;
+
+ /* Return the new type */
+ return T;
+}
+
+
+
+Type* NewPointerTo (const Type* T)
+/* Return a type string that is "pointer to T". The type string is allocated
+** on the heap and may be freed after use.
+*/
+{
+ /* Get the size of the type string including the terminator */
+ unsigned Size = TypeLen (T) + 1;
+
+ /* Allocate the new type string */
+ Type* P = TypeAlloc (Size + 1);
+
+ /* Create the return type... */
+ P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE);
+ memcpy (P+1, T, Size * sizeof (Type));
+
+ /* ...and return it */
+ return P;
+}
+
+
+
+Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth)
+/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type
+** string is allocated on the heap and may be freed after use.
+*/
+{
+ Type* P;
+
+ /* The type specifier must be integeral */
+ CHECK (IsClassInt (T));
+
+ /* Allocate the new type string */
+ P = TypeAlloc (3);
+
+ /* Create the return type... */
+ P[0].C = IsSignSigned (T) ? T_SBITFIELD : T_UBITFIELD;
+ P[0].C |= (T[0].C & T_QUAL_ADDRSIZE);
+ P[0].A.B.Offs = BitOffs;
+ P[0].A.B.Width = BitWidth;
+
+ /* Get the declaration type */
+ memcpy (&P[1], GetUnderlyingType (T), sizeof (P[1]));
+
+ /* Get done... */
+ P[2].C = T_END;
+
+ /* ...and return it */
+ return P;
+}
+
+
+
+const Type* AddressOf (const Type* T)
+/* Return a type string that is "address of T". The type string is allocated
+** on the heap and may be freed after use.
+*/
+{
+ /* Get the size of the type string including the terminator */
+ unsigned Size = TypeLen (T) + 1;
+
+ /* Allocate the new type string */
+ Type* P = TypeAlloc (Size + 1);
+
+ /* Create the return type... */
+ P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE) | T_QUAL_CONST;
+ memcpy (P+1, T, Size * sizeof (Type));
+
+ /* ...and return it */
+ return P;
}
@@ -939,48 +574,6 @@ Type* IndirectModifiable (Type* T)
-Type* NewPointerTo (const Type* T)
-/* Return a type string that is "pointer to T". The type string is allocated
-** on the heap and may be freed after use.
-*/
-{
- /* Get the size of the type string including the terminator */
- unsigned Size = TypeLen (T) + 1;
-
- /* Allocate the new type string */
- Type* P = TypeAlloc (Size + 1);
-
- /* Create the return type... */
- P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE);
- memcpy (P+1, T, Size * sizeof (Type));
-
- /* ...and return it */
- return P;
-}
-
-
-
-const Type* AddressOf (const Type* T)
-/* Return a type string that is "address of T". The type string is allocated
-** on the heap and may be freed after use.
-*/
-{
- /* Get the size of the type string including the terminator */
- unsigned Size = TypeLen (T) + 1;
-
- /* Allocate the new type string */
- Type* P = TypeAlloc (Size + 1);
-
- /* Create the return type... */
- P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE) | T_QUAL_CONST;
- memcpy (P+1, T, Size * sizeof (Type));
-
- /* ...and return it */
- return P;
-}
-
-
-
Type* ArrayToPtr (const Type* T)
/* Convert an array to a pointer to it's first element */
{
@@ -1181,37 +774,98 @@ const Type* UnsignedType (const Type* T)
-Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth)
-/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type
-** string is allocated on the heap and may be freed after use.
-*/
+const Type* GetUnderlyingType (const Type* Type)
+/* Get the underlying type of an enum or other integer class type */
{
- Type* P;
+ if (IsISOChar (Type)) {
+ return IS_Get (&SignedChars) ? type_schar : type_uchar;
+ } else if (IsTypeEnum (Type)) {
+ /* This should not happen, but just in case */
+ if (Type->A.S == 0) {
+ Internal ("Enum tag type error in GetUnderlyingTypeCode");
+ }
- /* The type specifier must be integeral */
- CHECK (IsClassInt (T));
+ /* If incomplete enum type is used, just return its raw type */
+ if (Type->A.S->V.E.Type != 0) {
+ return Type->A.S->V.E.Type;
+ }
+ } else if (IsTypeBitField (Type)) {
+ /* We consider the smallest type that can represent all values of the
+ ** bit-field, instead of the type used in the declaration, the truly
+ ** underlying of the bit-field.
+ */
+ switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
+ case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break;
+ case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break;
+ case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break;
+ default: Type = IsSignSigned (Type) ? type_int : type_uint; break;
+ }
+ }
- /* Allocate the new type string */
- P = TypeAlloc (3);
-
- /* Create the return type... */
- P[0].C = IsSignSigned (T) ? T_SBITFIELD : T_UBITFIELD;
- P[0].C |= (T[0].C & T_QUAL_ADDRSIZE);
- P[0].A.B.Offs = BitOffs;
- P[0].A.B.Width = BitWidth;
-
- /* Get the declaration type */
- memcpy (&P[1], GetUnderlyingType (T), sizeof (P[1]));
-
- /* Get done... */
- P[2].C = T_END;
-
- /* ...and return it */
- return P;
+ return Type;
}
+const Type* GetStructReplacementType (const Type* SType)
+/* Get a replacement type for passing a struct/union in the primary register */
+{
+ const Type* NewType;
+ /* If the size is less than or equal to that of a long, we will copy the
+ ** struct using the primary register, otherwise we will use memcpy.
+ */
+ switch (SizeOf (SType)) {
+ case 1: NewType = type_uchar; break;
+ case 2: NewType = type_uint; break;
+ case 3: /* FALLTHROUGH */
+ case 4: NewType = type_ulong; break;
+ default: NewType = SType; break;
+ }
+
+ return NewType;
+}
+
+
+
+const Type* GetBitFieldChunkType (const Type* Type)
+/* Get the type needed to operate on the byte chunk containing the bit-field */
+{
+ unsigned ChunkSize;
+ if ((Type->A.B.Width - 1U) / CHAR_BITS ==
+ (Type->A.B.Offs + Type->A.B.Width - 1U) / CHAR_BITS) {
+ /* T bit-field fits within its underlying type */
+ return GetUnderlyingType (Type);
+ }
+
+ ChunkSize = GetBitFieldMinimalTypeSize (Type->A.B.Offs + Type->A.B.Width);
+ if (ChunkSize < SizeOf (Type + 1)) {
+ /* The end of the bit-field is offset by some bits so that it requires
+ ** more bytes to be accessed as a whole than its underlying type does.
+ ** Note: In cc65 the bit offset is always less than CHAR_BITS.
+ */
+ switch (ChunkSize) {
+ case SIZEOF_CHAR: return IsSignSigned (Type) ? type_schar : type_uchar;
+ case SIZEOF_INT: return IsSignSigned (Type) ? type_int : type_uint;
+ case SIZEOF_LONG: return IsSignSigned (Type) ? type_long : type_ulong;
+ default: return IsSignSigned (Type) ? type_int : type_uint;
+ }
+ }
+
+ /* We can always use the declarartion integer type as the chunk type.
+ ** Note: A bit-field will not occupy bits located in bytes more than that
+ ** of its declaration type in cc65. So this is OK.
+ */
+ return Type + 1;
+}
+
+
+
+/*****************************************************************************/
+/* Type Predicates */
+/*****************************************************************************/
+
+
+
int IsTypeFragBitField (const Type* T)
/* Return true if this is a bit-field that shares byte space with other fields */
{
@@ -1307,9 +961,9 @@ int IsESUType (const Type* T)
int IsIncompleteESUType (const Type* T)
/* Return true if this is an incomplete ESU type */
{
- SymEntry* Sym = GetSymType (T);
+ SymEntry* TagSym = GetESUTagSym (T);
- return Sym != 0 && !SymIsDef (Sym);
+ return TagSym != 0 && !SymIsDef (TagSym);
}
@@ -1333,6 +987,46 @@ int HasUnknownSize (const Type* T)
+int TypeHasAttr (const Type* T)
+/* Return true if the given type has attribute data */
+{
+ return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T);
+}
+
+
+
+/*****************************************************************************/
+/* Qualifier helpers */
+/*****************************************************************************/
+
+
+
+TypeCode AddrSizeQualifier (unsigned AddrSize)
+/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */
+{
+ switch (AddrSize) {
+
+ case ADDR_SIZE_ABS:
+ return T_QUAL_NEAR;
+
+ case ADDR_SIZE_FAR:
+ return T_QUAL_FAR;
+
+ default:
+ Error ("Invalid address size");
+ return T_QUAL_NEAR;
+
+ }
+}
+
+
+
+/*****************************************************************************/
+/* Function type helpers */
+/*****************************************************************************/
+
+
+
int IsVariadicFunc (const Type* T)
/* Return true if this is a function type or pointer to function type with
** variable parameter list.
@@ -1441,6 +1135,12 @@ const FuncDesc* GetFuncDefinitionDesc (const Type* T)
+/*****************************************************************************/
+/* Array type helpers */
+/*****************************************************************************/
+
+
+
long GetElementCount (const Type* T)
/* Get the element count of the array specified in T (which must be of
** array type).
@@ -1486,20 +1186,27 @@ const Type* GetBaseElementType (const Type* T)
-struct SymEntry* GetESUSymEntry (const Type* T)
-/* Return a SymEntry pointer from an enum/struct/union type */
-{
- /* Only enums, structs or unions have a SymEntry attribute */
- CHECK (IsClassStruct (T) || IsTypeEnum (T));
+/*****************************************************************************/
+/* ESU types helpers */
+/*****************************************************************************/
- /* Return the attribute */
- return T->A.S;
+
+
+struct SymEntry* GetESUTagSym (const Type* T)
+/* Get the tag symbol entry of the enum/struct/union type.
+** Return 0 if it is not an enum/struct/union.
+*/
+{
+ if ((IsClassStruct (T) || IsTypeEnum (T))) {
+ return T->A.S;
+ }
+ return 0;
}
-void SetESUSymEntry (Type* T, struct SymEntry* S)
-/* Set the SymEntry pointer for an enum/struct/union type */
+void SetESUTagSym (Type* T, struct SymEntry* S)
+/* Set the tag symbol entry of the enum/struct/union type */
{
/* Only enums, structs or unions have a SymEntry attribute */
CHECK (IsClassStruct (T) || IsTypeEnum (T));
@@ -1510,30 +1217,319 @@ void SetESUSymEntry (Type* T, struct SymEntry* S)
-TypeCode AddrSizeQualifier (unsigned AddrSize)
-/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */
+/*****************************************************************************/
+/* Helpers */
+/*****************************************************************************/
+
+
+
+const char* GetBasicTypeName (const Type* T)
+/* Return a const name string of the basic type.
+** Return "type" for unknown basic types.
+*/
{
- switch (AddrSize) {
-
- case ADDR_SIZE_ABS:
- return T_QUAL_NEAR;
-
- case ADDR_SIZE_FAR:
- return T_QUAL_FAR;
-
- default:
- Error ("Invalid address size");
- return T_QUAL_NEAR;
-
+ switch (GetRawType (T)) {
+ case T_TYPE_ENUM: return "enum";
+ case T_TYPE_BITFIELD: return "bit-field";
+ case T_TYPE_FLOAT: return "float";
+ case T_TYPE_DOUBLE: return "double";
+ case T_TYPE_VOID: return "void";
+ case T_TYPE_STRUCT: return "struct";
+ case T_TYPE_UNION: return "union";
+ case T_TYPE_ARRAY: return "array";
+ case T_TYPE_PTR: return "pointer";
+ case T_TYPE_FUNC: return "function";
+ case T_TYPE_NONE: /* FALLTHROUGH */
+ default: break;
}
+ if (IsClassInt (T)) {
+ if (IsRawSignSigned (T)) {
+ switch (GetRawType (T)) {
+ case T_TYPE_CHAR: return "signed char";
+ case T_TYPE_SHORT: return "short";
+ case T_TYPE_INT: return "int";
+ case T_TYPE_LONG: return "long";
+ case T_TYPE_LONGLONG: return "long long";
+ default:
+ return "signed integer";
+ }
+ } else if (IsRawSignUnsigned (T)) {
+ switch (GetRawType (T)) {
+ case T_TYPE_CHAR: return "unsigned char";
+ case T_TYPE_SHORT: return "unsigned short";
+ case T_TYPE_INT: return "unsigned int";
+ case T_TYPE_LONG: return "unsigned long";
+ case T_TYPE_LONGLONG: return "unsigned long long";
+ default:
+ return "unsigned integer";
+ }
+ } else {
+ switch (GetRawType (T)) {
+ case T_TYPE_CHAR: return "char";
+ case T_TYPE_SHORT: return "short";
+ case T_TYPE_INT: return "int";
+ case T_TYPE_LONG: return "long";
+ case T_TYPE_LONGLONG: return "long long";
+ default:
+ return "integer";
+ }
+ }
+ }
+ return "type";
}
-int TypeHasAttr (const Type* T)
-/* Return true if the given type has attribute data */
+static const char* GetTagSymName (const Type* T)
+/* Return a name string of the type or the symbol name if it is an ESU type.
+** Note: This may use a static buffer that could be overwritten by other calls.
+*/
{
- return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T);
+ static char TypeName [IDENTSIZE + 16];
+ SymEntry* Sym;
+
+ Sym = GetESUTagSym (T);
+ if (Sym == 0) {
+ return GetBasicTypeName (T);
+ }
+ sprintf (TypeName, "%s %s", GetBasicTypeName (T),
+ Sym->Name[0] != '\0' ? Sym->Name : "");
+
+ return TypeName;
+}
+
+
+
+const char* GetFullTypeName (const Type* T)
+/* Return the full name string of the given type */
+{
+ struct StrBuf* Buf = NewDiagnosticStrBuf ();
+ GetFullTypeNameBuf (Buf, T);
+
+ return SB_GetConstBuf (Buf);
+}
+
+
+
+static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBuf* East, const Type* T)
+/* Return the name string of the given type split into a western part and an
+** eastern part.
+*/
+{
+ struct StrBuf Buf = AUTO_STRBUF_INITIALIZER;
+
+ if (IsTypeArray (T)) {
+
+ long Count = GetElementCount (T);
+ if (!SB_IsEmpty (East)) {
+ if (Count > 0) {
+ SB_Printf (&Buf, "[%ld]", Count);
+ } else {
+ SB_Printf (&Buf, "[]");
+ }
+ SB_Append (East, &Buf);
+ SB_Terminate (East);
+
+ } else {
+ if (Count > 0) {
+ SB_Printf (East, "[%ld]", Count);
+ } else {
+ SB_Printf (East, "[]");
+ }
+
+ if (!SB_IsEmpty (West)) {
+ /* Add parentheses to West */
+ SB_Printf (&Buf, "(%s)", SB_GetConstBuf (West));
+ SB_Copy (West, &Buf);
+ SB_Terminate (West);
+ }
+ }
+
+ /* Get element type */
+ GetFullTypeNameWestEast (West, East, T + 1);
+
+ } else if (IsTypeFunc (T)) {
+
+ FuncDesc* D = GetFuncDesc (T);
+ struct StrBuf ParamList = AUTO_STRBUF_INITIALIZER;
+
+ /* First argument */
+ SymEntry* Param = D->SymTab->SymHead;
+ unsigned I;
+ for (I = 0; I < D->ParamCount; ++I) {
+ CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0);
+ if (I > 0) {
+ SB_AppendStr (&ParamList, ", ");
+ }
+ SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type)));
+ SB_Clear (&Buf);
+ /* Next argument */
+ Param = Param->NextSym;
+ }
+ if ((D->Flags & FD_VARIADIC) == 0) {
+ if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) {
+ SB_AppendStr (&ParamList, "void");
+ }
+ } else {
+ if (D->ParamCount > 0) {
+ SB_AppendStr (&ParamList, ", ...");
+ } else {
+ SB_AppendStr (&ParamList, "...");
+ }
+ }
+ SB_Terminate (&ParamList);
+
+ /* Join the existing West and East together */
+ if (!SB_IsEmpty (East)) {
+ SB_Append (West, East);
+ SB_Terminate (West);
+ SB_Clear (East);
+ }
+
+ if (SB_IsEmpty (West)) {
+ /* Just use the param list */
+ SB_Printf (West, "(%s)", SB_GetConstBuf (&ParamList));
+ } else {
+ /* Append the param list to the existing West */
+ SB_Printf (&Buf, "(%s)(%s)", SB_GetConstBuf (West), SB_GetConstBuf (&ParamList));
+ SB_Printf (West, "%s", SB_GetConstBuf (&Buf));
+ }
+ SB_Done (&ParamList);
+
+ /* Return type */
+ GetFullTypeNameWestEast (West, East, T + 1);
+
+ } else if (IsTypePtr (T)) {
+
+ int QualCount = 0;
+
+ SB_Printf (&Buf, "*");
+
+ /* Add qualifiers */
+ if ((GetQualifier (T) & ~T_QUAL_NEAR) != T_QUAL_NONE) {
+ QualCount = GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR);
+ }
+
+ if (!SB_IsEmpty (West)) {
+ if (QualCount > 0) {
+ SB_AppendChar (&Buf, ' ');
+ }
+ SB_Append (&Buf, West);
+ }
+
+ SB_Copy (West, &Buf);
+ SB_Terminate (West);
+
+ /* Get indirection type */
+ GetFullTypeNameWestEast (West, East, T + 1);
+
+ } else {
+
+ /* Add qualifiers */
+ if ((GetQualifier (T) & ~T_QUAL_NEAR) != 0) {
+ if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR) > 0) {
+ SB_AppendChar (&Buf, ' ');
+ }
+ }
+
+ if (!IsTypeBitField (T)) {
+ SB_AppendStr (&Buf, GetTagSymName (T));
+ } else {
+ SB_AppendStr (&Buf, GetBasicTypeName (T + 1));
+ }
+
+ if (!SB_IsEmpty (West)) {
+ SB_AppendChar (&Buf, ' ');
+ SB_Append (&Buf, West);
+ }
+
+ SB_Copy (West, &Buf);
+ SB_Terminate (West);
+ }
+
+ SB_Done (&Buf);
+ return West;
+}
+
+
+
+struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T)
+/* Return the full name string of the given type */
+{
+ struct StrBuf East = AUTO_STRBUF_INITIALIZER;
+ GetFullTypeNameWestEast (S, &East, T);
+
+ /* Join West and East */
+ SB_Append (S, &East);
+ SB_Terminate (S);
+ SB_Done (&East);
+
+ return S;
+}
+
+
+
+int GetQualifierTypeCodeNameBuf (struct StrBuf* S, TypeCode Qual, TypeCode IgnoredQual)
+/* Return the names of the qualifiers of the type.
+** Qualifiers to be ignored can be specified with the IgnoredQual flags.
+** Return the count of added qualifier names.
+*/
+{
+ int Count = 0;
+
+ Qual &= T_MASK_QUAL & ~IgnoredQual;
+ if (Qual & T_QUAL_CONST) {
+ if (!SB_IsEmpty (S)) {
+ SB_AppendChar (S, ' ');
+ }
+ SB_AppendStr (S, "const");
+ ++Count;
+ }
+ if (Qual & T_QUAL_VOLATILE) {
+ if (Count > 0) {
+ SB_AppendChar (S, ' ');
+ }
+ SB_AppendStr (S, "volatile");
+ ++Count;
+ }
+ if (Qual & T_QUAL_RESTRICT) {
+ if (Count > 0) {
+ SB_AppendChar (S, ' ');
+ }
+ SB_AppendStr (S, "restrict");
+ ++Count;
+ }
+ if (Qual & T_QUAL_NEAR) {
+ if (Count > 0) {
+ SB_AppendChar (S, ' ');
+ }
+ SB_AppendStr (S, "__near__");
+ ++Count;
+ }
+ if (Qual & T_QUAL_FAR) {
+ SB_AppendStr (S, "__far__");
+ ++Count;
+ }
+ if (Qual & T_QUAL_FASTCALL) {
+ if (Count > 0) {
+ SB_AppendChar (S, ' ');
+ }
+ SB_AppendStr (S, "__fastcall__");
+ ++Count;
+ }
+ if (Qual & T_QUAL_CDECL) {
+ if (Count > 0) {
+ SB_AppendChar (S, ' ');
+ }
+ SB_AppendStr (S, "__cdecl__");
+ ++Count;
+ }
+
+ if (Count > 0) {
+ SB_Terminate (S);
+ }
+
+ return Count;
}
@@ -1570,7 +1566,7 @@ void PrintFuncSig (FILE* F, const char* Name, const Type* T)
if (SymIsRegVar (Param)) {
SB_AppendStr (&ParamList, "register ");
}
- if (!HasAnonName (Param)) {
+ if (!SymHasAnonName (Param)) {
SB_AppendStr (&Buf, Param->Name);
}
SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type)));
diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h
index e8ba7b6c0..4a20422fb 100644
--- a/src/cc65/datatype.h
+++ b/src/cc65/datatype.h
@@ -239,23 +239,6 @@ extern const Type type_c_void_p[];
-const char* GetBasicTypeName (const Type* T);
-/* Return a const name string of the basic type.
-** Return "type" for unknown basic types.
-*/
-
-const char* GetFullTypeName (const Type* T);
-/* Return the full name string of the given type */
-
-struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T);
-/* Return the full name string of the given type */
-
-int GetQualifierTypeCodeNameBuf (struct StrBuf* S, TypeCode Qual, TypeCode IgnoredQual);
-/* Return the names of the qualifiers of the type.
-** Qualifiers to be ignored can be specified with the IgnoredQual flags.
-** Return the count of added qualifier names.
-*/
-
unsigned TypeLen (const Type* T);
/* Return the length of the type string */
@@ -273,17 +256,26 @@ Type* TypeAlloc (unsigned Len);
void TypeFree (Type* T);
/* Free a type string */
+#if defined(HAVE_INLINE)
+INLINE void CopyTypeAttr (const Type* Src, Type* Dest)
+/* Copy attribute data from Src to Dest */
+{
+ Dest->A = Src->A;
+}
+#else
+# define CopyTypeAttr(Src, Dest) ((Dest)->A = (Src)->A)
+#endif
+
+
+
+/*****************************************************************************/
+/* Type info extraction */
+/*****************************************************************************/
+
+
+
int SignExtendChar (int C);
-/* Do correct sign extension of a character */
-
-Type* GetCharArrayType (unsigned Len);
-/* Return the type for a char array of the given length */
-
-Type* GetImplicitFuncType (void);
-/* Return a type string for an inplicitly declared function */
-
-const Type* GetStructReplacementType (const Type* SType);
-/* Get a replacement type for passing a struct/union in the primary register */
+/* Do correct sign extension of a character to an int */
long GetIntegerTypeMin (const Type* Type);
/* Get the smallest possible value of the integer type.
@@ -295,9 +287,51 @@ unsigned long GetIntegerTypeMax (const Type* Type);
** The type must have a known size.
*/
+unsigned BitSizeOf (const Type* T);
+/* Return the size (in bit-width) of a data type */
+
+unsigned SizeOf (const Type* T);
+/* Compute size (in bytes) of object represented by type array */
+
+unsigned PSizeOf (const Type* T);
+/* Compute size (in bytes) of pointee object */
+
+unsigned CheckedBitSizeOf (const Type* T);
+/* Return the size (in bit-width) of a data type. If the size is zero, emit an
+** error and return some valid size instead (so the rest of the compiler
+** doesn't have to work with invalid sizes).
+*/
+
+unsigned CheckedSizeOf (const Type* T);
+/* Return the size (in bytes) of a data type. If the size is zero, emit an
+** error and return some valid size instead (so the rest of the compiler
+** doesn't have to work with invalid sizes).
+*/
+
+unsigned CheckedPSizeOf (const Type* T);
+/* Return the size (in bytes) of a data type that is pointed to by a pointer.
+** If the size is zero, emit an error and return some valid size instead (so
+** the rest of the compiler doesn't have to work with invalid sizes).
+*/
+
+#if defined(HAVE_INLINE)
+INLINE TypeCode GetQualifier (const Type* T)
+/* Get the qualifier from the given type string */
+{
+ return (T->C & T_MASK_QUAL);
+}
+#else
+# define GetQualifier(T) ((T)->C & T_MASK_QUAL)
+#endif
+
+TypeCode GetUnderlyingTypeCode (const Type* Type);
+/* Get the type code of the unqualified underlying type of TCode.
+** Return TCode if it is not scalar.
+*/
+
#if defined(HAVE_INLINE)
INLINE TypeCode UnqualifiedType (TypeCode T)
-/* Return the unqalified type code */
+/* Return the unqualified type code */
{
return (T & ~T_MASK_QUAL);
}
@@ -305,39 +339,94 @@ INLINE TypeCode UnqualifiedType (TypeCode T)
# define UnqualifiedType(T) ((T) & ~T_MASK_QUAL)
#endif
-const Type* GetUnderlyingType (const Type* Type);
-/* Get the underlying type of an enum or other integer class type */
+#if defined(HAVE_INLINE)
+INLINE TypeCode GetClass (const Type* T)
+/* Get the class of a type string */
+{
+ return (T->C & T_MASK_CLASS);
+}
+#else
+# define GetClass(T) ((T)->C & T_MASK_CLASS)
+#endif
-TypeCode GetUnderlyingTypeCode (const Type* Type);
-/* Get the type code of the unqualified underlying type of TCode.
-** Return TCode if it is not scalar.
+#if defined(HAVE_INLINE)
+INLINE TypeCode GetSignedness (const Type* T)
+/* Get the signedness of a type */
+{
+ return (GetUnderlyingTypeCode (T) & T_MASK_SIGN);
+}
+#else
+# define GetSignedness(T) (GetUnderlyingTypeCode (T) & T_MASK_SIGN)
+#endif
+
+#if defined(HAVE_INLINE)
+INLINE TypeCode GetSizeModifier (const Type* T)
+/* Get the size modifier of a type */
+{
+ return (GetUnderlyingTypeCode (T) & T_MASK_SIZE);
+}
+#else
+# define GetSizeModifier(T) (GetUnderlyingTypeCode (T) & T_MASK_SIZE)
+#endif
+
+#if defined(HAVE_INLINE)
+INLINE TypeCode GetRawType (const Type* T)
+/* Get the raw type */
+{
+ return (T->C & T_MASK_TYPE);
+}
+#else
+# define GetRawType(T) ((T)->C & T_MASK_TYPE)
+#endif
+
+#if defined(HAVE_INLINE)
+INLINE TypeCode GetRawSignedness (const Type* T)
+/* Get the raw signedness of a type */
+{
+ return ((T)->C & T_MASK_SIGN);
+}
+#else
+# define GetRawSignedness(T) ((T)->C & T_MASK_SIGN)
+#endif
+
+#if defined(HAVE_INLINE)
+INLINE TypeCode GetRawSizeModifier (const Type* T)
+/* Get the size modifier of a raw type */
+{
+ return (T->C & T_MASK_SIZE);
+}
+#else
+# define GetRawSizeModifier(T) ((T)->C & T_MASK_SIZE)
+#endif
+
+
+
+/*****************************************************************************/
+/* Type manipulation */
+/*****************************************************************************/
+
+
+
+Type* GetImplicitFuncType (void);
+/* Return a type string for an implicitly declared function */
+
+Type* GetCharArrayType (unsigned Len);
+/* Return the type for a char array of the given length */
+
+Type* NewPointerTo (const Type* T);
+/* Return a type string that is "pointer to T". The type string is allocated
+** on the heap and may be freed after use.
*/
-const Type* GetBitFieldChunkType (const Type* Type);
-/* Get the type needed to operate on the byte chunk containing the bit-field */
-
-unsigned SizeOf (const Type* T);
-/* Compute size of object represented by type array. */
-
-unsigned PSizeOf (const Type* T);
-/* Compute size of pointer object. */
-
-unsigned CheckedSizeOf (const Type* T);
-/* Return the size of a data type. If the size is zero, emit an error and
-** return some valid size instead (so the rest of the compiler doesn't have
-** to work with invalid sizes).
-*/
-unsigned CheckedPSizeOf (const Type* T);
-/* Return the size of a data type that is pointed to by a pointer. If the
-** size is zero, emit an error and return some valid size instead (so the
-** rest of the compiler doesn't have to work with invalid sizes).
+Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth);
+/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type
+** string is allocated on the heap and may be freed after use.
*/
-unsigned TypeOf (const Type* T);
-/* Get the code generator base type of the object */
-
-unsigned FuncTypeOf (const Type* T);
-/* Get the code generator flag for calling the function */
+const Type* AddressOf (const Type* T);
+/* Return a type string that is "address of T". The type string is allocated
+** on the heap and may be freed after use.
+*/
const Type* Indirect (const Type* T);
/* Do one indirection for the given type, that is, return the type where the
@@ -349,16 +438,6 @@ Type* IndirectModifiable (Type* T);
** given type points to.
*/
-Type* NewPointerTo (const Type* T);
-/* Return a type string that is "pointer to T". The type string is allocated
-** on the heap and may be freed after use.
-*/
-
-const Type* AddressOf (const Type* T);
-/* Return a type string that is "address of T". The type string is allocated
-** on the heap and may be freed after use.
-*/
-
Type* ArrayToPtr (const Type* T);
/* Convert an array to a pointer to it's first element */
@@ -388,20 +467,22 @@ const Type* SignedType (const Type* T);
const Type* UnsignedType (const Type* T);
/* Get unsigned counterpart of the integral type */
-Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth);
-/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type
-** string is allocated on the heap and may be freed after use.
-*/
+const Type* GetUnderlyingType (const Type* Type);
+/* Get the underlying type of an enum or other integer class type */
+
+const Type* GetStructReplacementType (const Type* SType);
+/* Get a replacement type for passing a struct/union in the primary register */
+
+const Type* GetBitFieldChunkType (const Type* Type);
+/* Get the type needed to operate on the byte chunk containing the bit-field */
+
+
+
+/*****************************************************************************/
+/* Type Predicates */
+/*****************************************************************************/
+
-#if defined(HAVE_INLINE)
-INLINE TypeCode GetRawType (const Type* T)
-/* Get the raw type */
-{
- return (T->C & T_MASK_TYPE);
-}
-#else
-# define GetRawType(T) ((T)->C & T_MASK_TYPE)
-#endif
#if defined(HAVE_INLINE)
INLINE int IsTypeChar (const Type* T)
@@ -420,7 +501,7 @@ INLINE int IsTypeShort (const Type* T)
return (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT);
}
#else
-# define IsTypeShort(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT)
+# define IsTypeShort(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT)
#endif
#if defined(HAVE_INLINE)
@@ -585,7 +666,7 @@ INLINE int IsTypeUnion (const Type* T)
return (GetRawType (T) == T_TYPE_UNION);
}
#else
-# define IsTypeUnion(T) (GetRawType (T) == T_TYPE_UNION)
+# define IsTypeUnion(T) (GetRawType (T) == T_TYPE_UNION)
#endif
#if defined(HAVE_INLINE)
@@ -628,16 +709,6 @@ INLINE int IsTypeFuncPtr (const Type* T)
# define IsTypeFuncPtr(T) (IsTypePtr (T) && IsTypeFunc (T+1))
#endif
-#if defined(HAVE_INLINE)
-INLINE TypeCode GetClass (const Type* T)
-/* Get the class of a type string */
-{
- return (T->C & T_MASK_CLASS);
-}
-#else
-# define GetClass(T) ((T)->C & T_MASK_CLASS)
-#endif
-
#if defined(HAVE_INLINE)
INLINE int IsClassInt (const Type* T)
/* Return true if this is an integer type */
@@ -727,25 +798,8 @@ int IsEmptiableObjectType (const Type* T);
int HasUnknownSize (const Type* T);
/* Return true if this is an incomplete ESU type or an array of unknown size */
-#if defined(HAVE_INLINE)
-INLINE TypeCode GetRawSignedness (const Type* T)
-/* Get the raw signedness of a type */
-{
- return ((T)->C & T_MASK_SIGN);
-}
-#else
-# define GetRawSignedness(T) ((T)->C & T_MASK_SIGN)
-#endif
-
-#if defined(HAVE_INLINE)
-INLINE TypeCode GetSignedness (const Type* T)
-/* Get the signedness of a type */
-{
- return (GetUnderlyingTypeCode (T) & T_MASK_SIGN);
-}
-#else
-# define GetSignedness(T) (GetUnderlyingTypeCode (T) & T_MASK_SIGN)
-#endif
+int TypeHasAttr (const Type* T);
+/* Return true if the given type has attribute data */
#if defined(HAVE_INLINE)
INLINE int IsRawSignUnsigned (const Type* T)
@@ -787,35 +841,13 @@ INLINE int IsSignSigned (const Type* T)
# define IsSignSigned(T) (GetSignedness (T) == T_SIGN_SIGNED)
#endif
-#if defined(HAVE_INLINE)
-INLINE TypeCode GetRawSizeModifier (const Type* T)
-/* Get the size modifier of a raw type */
-{
- return (T->C & T_MASK_SIZE);
-}
-#else
-# define GetRawSizeModifier(T) ((T)->C & T_MASK_SIZE)
-#endif
-#if defined(HAVE_INLINE)
-INLINE TypeCode GetSizeModifier (const Type* T)
-/* Get the size modifier of a type */
-{
- return (GetUnderlyingTypeCode (T) & T_MASK_SIZE);
-}
-#else
-# define GetSizeModifier(T) (GetUnderlyingTypeCode (T) & T_MASK_SIZE)
-#endif
-#if defined(HAVE_INLINE)
-INLINE TypeCode GetQualifier (const Type* T)
-/* Get the qualifier from the given type string */
-{
- return (T->C & T_MASK_QUAL);
-}
-#else
-# define GetQualifier(T) ((T)->C & T_MASK_QUAL)
-#endif
+/*****************************************************************************/
+/* Qualifier helpers */
+/*****************************************************************************/
+
+
#if defined(HAVE_INLINE)
INLINE int IsQualConst (const Type* T)
@@ -897,6 +929,37 @@ INLINE int IsQualCConv (const Type* T)
# define IsQualCConv(T) (((T)->C & T_QUAL_CCONV) != 0)
#endif
+TypeCode AddrSizeQualifier (unsigned AddrSize);
+/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */
+
+#if defined(HAVE_INLINE)
+INLINE TypeCode CodeAddrSizeQualifier (void)
+/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the code address size */
+{
+ return AddrSizeQualifier (CodeAddrSize);
+}
+#else
+# define CodeAddrSizeQualifier() (AddrSizeQualifier (CodeAddrSize))
+#endif
+
+#if defined(HAVE_INLINE)
+INLINE TypeCode DataAddrSizeQualifier (void)
+/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the data address size */
+{
+ return AddrSizeQualifier (DataAddrSize);
+}
+#else
+# define DataAddrSizeQualifier() (AddrSizeQualifier (DataAddrSize))
+#endif
+
+
+
+/*****************************************************************************/
+/* Function type helpers */
+/*****************************************************************************/
+
+
+
int IsVariadicFunc (const Type* T) attribute ((const));
/* Return true if this is a function type or pointer to function type with
** variable parameter list.
@@ -924,6 +987,14 @@ Type* GetFuncReturnModifiable (Type* T) attribute ((const));
const FuncDesc* GetFuncDefinitionDesc (const Type* T) attribute ((const));
/* Get the function descriptor of the function definition */
+
+
+/*****************************************************************************/
+/* Array type helpers */
+/*****************************************************************************/
+
+
+
long GetElementCount (const Type* T);
/* Get the element count of the array specified in T (which must be of
** array type).
@@ -943,47 +1014,46 @@ const Type* GetBaseElementType (const Type* T);
** the element type that is not an array.
*/
-struct SymEntry* GetESUSymEntry (const Type* T) attribute ((const));
-/* Return a SymEntry pointer from an enum/struct/union type */
-void SetESUSymEntry (Type* T, struct SymEntry* S);
-/* Set the SymEntry pointer for an enum/struct/union type */
-TypeCode AddrSizeQualifier (unsigned AddrSize);
-/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */
+/*****************************************************************************/
+/* ESU types helpers */
+/*****************************************************************************/
-#if defined(HAVE_INLINE)
-INLINE TypeCode CodeAddrSizeQualifier (void)
-/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the code address size */
-{
- return AddrSizeQualifier (CodeAddrSize);
-}
-#else
-# define CodeAddrSizeQualifier() (AddrSizeQualifier (CodeAddrSize))
-#endif
-#if defined(HAVE_INLINE)
-INLINE TypeCode DataAddrSizeQualifier (void)
-/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the data address size */
-{
- return AddrSizeQualifier (DataAddrSize);
-}
-#else
-# define DataAddrSizeQualifier() (AddrSizeQualifier (DataAddrSize))
-#endif
-int TypeHasAttr (const Type* T);
-/* Return true if the given type has attribute data */
+struct SymEntry* GetESUTagSym (const Type* T) attribute ((const));
+/* Get the tag symbol entry of the enum/struct/union type.
+** Return 0 if it is not an enum/struct/union.
+*/
-#if defined(HAVE_INLINE)
-INLINE void CopyTypeAttr (const Type* Src, Type* Dest)
-/* Copy attribute data from Src to Dest */
-{
- Dest->A = Src->A;
-}
-#else
-# define CopyTypeAttr(Src, Dest) ((Dest)->A = (Src)->A)
-#endif
+void SetESUTagSym (Type* T, struct SymEntry* S);
+/* Set the tag symbol entry of the enum/struct/union type */
+
+
+
+/*****************************************************************************/
+/* Helpers */
+/*****************************************************************************/
+
+
+
+const char* GetBasicTypeName (const Type* T);
+/* Return a const name string of the basic type.
+** Return "type" for unknown basic types.
+*/
+
+const char* GetFullTypeName (const Type* T);
+/* Return the full name string of the given type */
+
+struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T);
+/* Return the full name string of the given type */
+
+int GetQualifierTypeCodeNameBuf (struct StrBuf* S, TypeCode Qual, TypeCode IgnoredQual);
+/* Return the names of the qualifiers of the type.
+** Qualifiers to be ignored can be specified with the IgnoredQual flags.
+** Return the count of added qualifier names.
+*/
void PrintType (FILE* F, const Type* T);
/* Print fulle name of the type */
diff --git a/src/cc65/declare.c b/src/cc65/declare.c
index 7cc7444b6..459ffa103 100644
--- a/src/cc65/declare.c
+++ b/src/cc65/declare.c
@@ -72,8 +72,7 @@
-static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
- int* SignednessSpecified);
+static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified);
/* Parse a type specifier */
@@ -84,6 +83,75 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
+static unsigned ParseOneStorageClass (void)
+/* Parse and return a storage class specifier */
+{
+ unsigned StorageClass = 0;
+
+ /* Check the storage class given */
+ switch (CurTok.Tok) {
+
+ case TOK_EXTERN:
+ StorageClass = SC_EXTERN | SC_STATIC;
+ NextToken ();
+ break;
+
+ case TOK_STATIC:
+ StorageClass = SC_STATIC;
+ NextToken ();
+ break;
+
+ case TOK_REGISTER:
+ StorageClass = SC_REGISTER | SC_STATIC;
+ NextToken ();
+ break;
+
+ case TOK_AUTO:
+ StorageClass = SC_AUTO;
+ NextToken ();
+ break;
+
+ case TOK_TYPEDEF:
+ StorageClass = SC_TYPEDEF;
+ NextToken ();
+ break;
+
+ default:
+ break;
+ }
+
+ return StorageClass;
+}
+
+
+
+static int ParseStorageClass (DeclSpec* D)
+/* Parse storage class specifiers. Return true if a specifier is read even if
+** it was duplicated or disallowed. */
+{
+ /* Check the storage class given */
+ unsigned StorageClass = ParseOneStorageClass ();
+
+ if (StorageClass == 0) {
+ return 0;
+ }
+
+ while (StorageClass != 0) {
+ if (D->StorageClass == 0) {
+ D->StorageClass = StorageClass;
+ } else if (D->StorageClass == StorageClass) {
+ Warning ("Duplicate storage class specifier");
+ } else {
+ Error ("Conflicting storage class specifier");
+ }
+ StorageClass = ParseOneStorageClass ();
+ }
+
+ return 1;
+}
+
+
+
static void DuplicateQualifier (const char* Name)
/* Print an error message */
{
@@ -92,9 +160,9 @@ static void DuplicateQualifier (const char* Name)
-static TypeCode OptionalQualifiers (TypeCode Allowed)
+static TypeCode OptionalQualifiers (TypeCode Qualifiers, TypeCode Allowed)
/* Read type qualifiers if we have any. Allowed specifies the allowed
-** qualifiers.
+** qualifiers. Return any read qualifiers even if they caused errors.
*/
{
/* We start without any qualifiers */
@@ -107,7 +175,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_CONST:
if (Allowed & T_QUAL_CONST) {
- if (Q & T_QUAL_CONST) {
+ if (Qualifiers & T_QUAL_CONST) {
DuplicateQualifier ("const");
}
Q |= T_QUAL_CONST;
@@ -118,7 +186,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_VOLATILE:
if (Allowed & T_QUAL_VOLATILE) {
- if (Q & T_QUAL_VOLATILE) {
+ if (Qualifiers & T_QUAL_VOLATILE) {
DuplicateQualifier ("volatile");
}
Q |= T_QUAL_VOLATILE;
@@ -129,7 +197,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_RESTRICT:
if (Allowed & T_QUAL_RESTRICT) {
- if (Q & T_QUAL_RESTRICT) {
+ if (Qualifiers & T_QUAL_RESTRICT) {
DuplicateQualifier ("restrict");
}
Q |= T_QUAL_RESTRICT;
@@ -140,7 +208,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_NEAR:
if (Allowed & T_QUAL_NEAR) {
- if (Q & T_QUAL_NEAR) {
+ if (Qualifiers & T_QUAL_NEAR) {
DuplicateQualifier ("near");
}
Q |= T_QUAL_NEAR;
@@ -151,7 +219,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_FAR:
if (Allowed & T_QUAL_FAR) {
- if (Q & T_QUAL_FAR) {
+ if (Qualifiers & T_QUAL_FAR) {
DuplicateQualifier ("far");
}
Q |= T_QUAL_FAR;
@@ -162,7 +230,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_FASTCALL:
if (Allowed & T_QUAL_FASTCALL) {
- if (Q & T_QUAL_FASTCALL) {
+ if (Qualifiers & T_QUAL_FASTCALL) {
DuplicateQualifier ("fastcall");
}
Q |= T_QUAL_FASTCALL;
@@ -173,7 +241,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_CDECL:
if (Allowed & T_QUAL_CDECL) {
- if (Q & T_QUAL_CDECL) {
+ if (Qualifiers & T_QUAL_CDECL) {
DuplicateQualifier ("cdecl");
}
Q |= T_QUAL_CDECL;
@@ -187,13 +255,16 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
}
+ /* Combine with newly read qualifiers */
+ Qualifiers |= Q;
+
/* Skip the token */
NextToken ();
}
Done:
/* We cannot have more than one address size far qualifier */
- switch (Q & T_QUAL_ADDRSIZE) {
+ switch (Qualifiers & T_QUAL_ADDRSIZE) {
case T_QUAL_NONE:
case T_QUAL_NEAR:
@@ -202,11 +273,11 @@ Done:
default:
Error ("Cannot specify more than one address size qualifier");
- Q &= ~T_QUAL_ADDRSIZE;
+ Qualifiers &= ~T_QUAL_ADDRSIZE;
}
/* We cannot have more than one calling convention specifier */
- switch (Q & T_QUAL_CCONV) {
+ switch (Qualifiers & T_QUAL_CCONV) {
case T_QUAL_NONE:
case T_QUAL_FASTCALL:
@@ -215,15 +286,41 @@ Done:
default:
Error ("Cannot specify more than one calling convention qualifier");
- Q &= ~T_QUAL_CCONV;
+ Qualifiers &= ~T_QUAL_CCONV;
}
- /* Return the qualifiers read */
+ /* Return any qualifiers just read */
return Q;
}
+static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t TSFlags)
+/* Read storage specifiers and/or type qualifiers if we have any. Storage class
+** specifiers require the corresponding typespec_t flag set to be allowed, and
+** only const and volatile type qualifiers are allowed under any circumstance.
+** Read storage class specifiers are output in *Spec and type qualifiers are
+** output in *Qualifiers with error checking.
+*/
+{
+ TypeCode Q = T_QUAL_NONE;
+ int Continue;
+
+ do {
+ /* There may be type qualifiers *before* any storage class specifiers */
+ Q = OptionalQualifiers (*Qualifiers, T_QUAL_CONST | T_QUAL_VOLATILE);
+ *Qualifiers |= Q;
+
+ /* Parse storage class specifiers anyway then check */
+ Continue = ParseStorageClass (Spec);
+ if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) {
+ Error ("Unexpected storage class specified");
+ }
+ } while (Continue || Q != T_QUAL_NONE);
+}
+
+
+
static void OptionalInt (void)
/* Eat an optional "int" token */
{
@@ -259,8 +356,8 @@ void InitDeclSpec (DeclSpec* D)
-static void InitDeclaration (Declaration* D)
-/* Initialize the Declaration struct for use */
+static void InitDeclarator (Declarator* D)
+/* Initialize the Declarator struct for use */
{
D->Ident[0] = '\0';
D->Type[0].C = T_END;
@@ -270,7 +367,7 @@ static void InitDeclaration (Declaration* D)
-static void NeedTypeSpace (Declaration* D, unsigned Count)
+static void NeedTypeSpace (Declarator* D, unsigned Count)
/* Check if there is enough space for Count type specifiers within D */
{
if (D->Index + Count >= MAXTYPELEN) {
@@ -284,8 +381,8 @@ static void NeedTypeSpace (Declaration* D, unsigned Count)
-static void AddTypeToDeclaration (Declaration* D, TypeCode T)
-/* Add a type specifier to the type of a declaration */
+static void AddTypeCodeToDeclarator (Declarator* D, TypeCode T)
+/* Add a type specifier to the type of a declarator */
{
NeedTypeSpace (D, 1);
D->Type[D->Index++].C = T;
@@ -396,48 +493,6 @@ static void FixQualifiers (Type* DataType)
-static unsigned ParseOneStorageClass (void)
-/* Parse and return a storage class */
-{
- unsigned StorageClass = 0;
-
- /* Check the storage class given */
- switch (CurTok.Tok) {
-
- case TOK_EXTERN:
- StorageClass = SC_EXTERN | SC_STATIC;
- NextToken ();
- break;
-
- case TOK_STATIC:
- StorageClass = SC_STATIC;
- NextToken ();
- break;
-
- case TOK_REGISTER:
- StorageClass = SC_REGISTER | SC_STATIC;
- NextToken ();
- break;
-
- case TOK_AUTO:
- StorageClass = SC_AUTO;
- NextToken ();
- break;
-
- case TOK_TYPEDEF:
- StorageClass = SC_TYPEDEF;
- NextToken ();
- break;
-
- default:
- break;
- }
-
- return StorageClass;
-}
-
-
-
static void CheckArrayElementType (Type* DataType)
/* Check if data type consists of arrays of incomplete element types */
{
@@ -469,51 +524,24 @@ static void CheckArrayElementType (Type* DataType)
-static void ParseStorageClass (DeclSpec* D, unsigned DefStorage)
-/* Parse a storage class */
-{
- /* Assume we're using an explicit storage class */
- D->Flags &= ~DS_DEF_STORAGE;
-
- /* Check the storage class given */
- D->StorageClass = ParseOneStorageClass ();
- if (D->StorageClass == 0) {
- /* No storage class given, use default */
- D->Flags |= DS_DEF_STORAGE;
- D->StorageClass = DefStorage;
- } else {
- unsigned StorageClass = ParseOneStorageClass ();
- while (StorageClass != 0) {
- if (D->StorageClass == StorageClass) {
- Warning ("Duplicate storage class specifier");
- } else {
- Error ("Conflicting storage class specifier");
- }
- StorageClass = ParseOneStorageClass ();
- }
- }
-}
-
-
-
-static SymEntry* ESUForwardDecl (const char* Name, unsigned Flags, unsigned* DSFlags)
-/* Handle an enum, struct or union forward decl */
+static SymEntry* ForwardESU (const char* Name, unsigned Flags, unsigned* DSFlags)
+/* Handle an enum, struct or union forward declaration */
{
/* Try to find an enum/struct/union with the given name. If there is none,
** insert a forward declaration into the current lexical level.
*/
- SymEntry* Entry = FindTagSym (Name);
- if (Entry == 0) {
+ SymEntry* TagEntry = FindTagSym (Name);
+ if (TagEntry == 0) {
if ((Flags & SC_ESUTYPEMASK) != SC_ENUM) {
- Entry = AddStructSym (Name, Flags, 0, 0, DSFlags);
+ TagEntry = AddStructSym (Name, Flags, 0, 0, DSFlags);
} else {
- Entry = AddEnumSym (Name, Flags, 0, 0, DSFlags);
+ TagEntry = AddEnumSym (Name, Flags, 0, 0, DSFlags);
}
- } else if ((Entry->Flags & SC_TYPEMASK) != (Flags & SC_ESUTYPEMASK)) {
+ } else if ((TagEntry->Flags & SC_TYPEMASK) != (Flags & SC_ESUTYPEMASK)) {
/* Already defined, but not the same type class */
Error ("Symbol '%s' is already different kind", Name);
}
- return Entry;
+ return TagEntry;
}
@@ -556,8 +584,8 @@ static const Type* GetEnumeratorType (long Min, unsigned long Max, int Signed)
-static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags)
-/* Process an enum declaration */
+static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags)
+/* Process an enum specifier */
{
SymTable* FieldTab;
long EnumVal;
@@ -574,7 +602,7 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags)
if (CurTok.Tok != TOK_LCURLY) {
/* Just a forward definition */
- return ESUForwardDecl (Name, SC_ENUM, DSFlags);
+ return ForwardESU (Name, SC_ENUM, DSFlags);
}
/* Add a forward declaration for the enum tag in the current lexical level */
@@ -611,20 +639,20 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags)
} else {
- /* Defaulted with the same signedness as the previous member's */
+ /* Defaulted with the same signedness as the previous member's */
IsSigned = IsSignSigned (MemberType) &&
(unsigned long)EnumVal != GetIntegerTypeMax (MemberType);
- /* Enumerate. Signed integer overflow is UB but unsigned integers
- ** are guaranteed to wrap around.
- */
- EnumVal = (long)((unsigned long)EnumVal + 1UL);
+ /* Enumerate by adding one to the previous value */
+ EnumVal = (long)(((unsigned long)EnumVal + 1UL) & 0xFFFFFFFFUL);
if (UnqualifiedType (MemberType->C) == T_ULONG && EnumVal == 0) {
- /* Warn on 'unsigned long' overflow in enumeration */
- Warning ("Enumerator '%s' overflows the range of '%s'",
- Ident,
- GetBasicTypeName (type_ulong));
+ /* Error since the new value cannot be represented in the
+ ** largest unsigned integer type supported by cc65 for enum.
+ */
+ Error ("Enumerator '%s' overflows the range of '%s'",
+ Ident,
+ GetBasicTypeName (type_ulong));
}
IsIncremented = 1;
@@ -657,11 +685,12 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags)
/* Warn if the incremented value exceeds the range of the previous
** type.
*/
- if (IsIncremented &&
- EnumVal >= 0 &&
+ if (PrevErrorCount == ErrorCount &&
+ IsIncremented &&
+ (!IsSigned || EnumVal >= 0) &&
NewType->C != UnqualifiedType (MemberType->C)) {
/* The possible overflow here can only be when EnumVal > 0 */
- Warning ("Enumerator '%s' (value = %lu) is of type '%s'",
+ Warning ("Enumerator '%s' (value = %lu) implies type '%s'",
Ident,
(unsigned long)EnumVal,
GetBasicTypeName (NewType));
@@ -725,7 +754,7 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags)
-static int ParseFieldWidth (Declaration* D)
+static int ParseFieldWidth (Declarator* D)
/* Parse an optional field width. Returns -1 if no field width is specified,
** otherwise the width of the field.
*/
@@ -803,21 +832,19 @@ static unsigned PadWithBitField (unsigned StructSize, unsigned BitOffs)
-static unsigned AliasAnonStructFields (const Declaration* D, SymEntry* Anon)
+static unsigned AliasAnonStructFields (const Declarator* D, SymEntry* Anon)
/* Create alias fields from an anon union/struct in the current lexical level.
** The function returns the count of created aliases.
*/
{
unsigned Count = 0;
+ SymEntry* Field;
SymEntry* Alias;
- /* Get the pointer to the symbol table entry of the anon struct */
- SymEntry* Entry = GetESUSymEntry (D->Type);
-
/* Get the symbol table containing the fields. If it is empty, there has
** been an error before, so bail out.
*/
- SymTable* Tab = Entry->V.S.SymTab;
+ SymTable* Tab = GetESUTagSym (D->Type)->V.S.SymTab;
if (Tab == 0) {
/* Incomplete definition - has been flagged before */
return 0;
@@ -826,24 +853,24 @@ static unsigned AliasAnonStructFields (const Declaration* D, SymEntry* Anon)
/* Get a pointer to the list of symbols. Then walk the list adding copies
** of the embedded struct to the current level.
*/
- Entry = Tab->SymHead;
- while (Entry) {
+ Field = Tab->SymHead;
+ while (Field) {
/* Enter an alias of this symbol */
- if (!IsAnonName (Entry->Name)) {
- Alias = AddLocalSym (Entry->Name, Entry->Type, SC_STRUCTFIELD|SC_ALIAS, 0);
- Alias->V.A.Field = Entry;
- Alias->V.A.Offs = Anon->V.Offs + Entry->V.Offs;
+ if (!IsAnonName (Field->Name)) {
+ Alias = AddLocalSym (Field->Name, Field->Type, SC_STRUCTFIELD|SC_ALIAS, 0);
+ Alias->V.A.Field = Field;
+ Alias->V.A.Offs = Anon->V.Offs + Field->V.Offs;
++Count;
}
/* Currently, there can not be any attributes, but if there will be
** some in the future, we want to know this.
*/
- CHECK (Entry->Attr == 0);
+ CHECK (Field->Attr == 0);
/* Next entry */
- Entry = Entry->NextSym;
+ Field = Field->NextSym;
}
/* Return the count of created aliases */
@@ -852,8 +879,8 @@ static unsigned AliasAnonStructFields (const Declaration* D, SymEntry* Anon)
-static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags)
-/* Parse a union declaration. */
+static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
+/* Parse a union specifier */
{
unsigned UnionSize;
@@ -861,14 +888,14 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags)
int FieldWidth; /* Width in bits, -1 if not a bit-field */
SymTable* FieldTab;
SymEntry* UnionTagEntry;
- SymEntry* Entry;
+ SymEntry* Field;
unsigned Flags = 0;
unsigned PrevErrorCount = ErrorCount;
if (CurTok.Tok != TOK_LCURLY) {
/* Just a forward declaration */
- return ESUForwardDecl (Name, SC_UNION, DSFlags);
+ return ForwardESU (Name, SC_UNION, DSFlags);
}
/* Add a forward declaration for the union tag in the current lexical level */
@@ -883,19 +910,26 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags)
EnterStructLevel ();
/* Parse union fields */
- UnionSize = 0;
+ UnionSize = 0;
while (CurTok.Tok != TOK_RCURLY) {
/* Get the type of the entry */
DeclSpec Spec;
int SignednessSpecified = 0;
+
+ /* Check for a _Static_assert */
+ if (CurTok.Tok == TOK_STATIC_ASSERT) {
+ ParseStaticAssert ();
+ continue;
+ }
+
InitDeclSpec (&Spec);
- ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified);
+ ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified);
/* Read fields with this type */
while (1) {
- Declaration Decl;
+ Declarator Decl;
/* Get type and name of the struct field */
ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT);
@@ -945,17 +979,17 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags)
AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth,
SignednessSpecified);
} else if (Decl.Ident[0] != '\0') {
- Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0);
+ Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0);
if (IsAnonName (Decl.Ident)) {
- Entry->V.A.ANumber = UnionTagEntry->V.S.ACount++;
- AliasAnonStructFields (&Decl, Entry);
+ Field->V.A.ANumber = UnionTagEntry->V.S.ACount++;
+ AliasAnonStructFields (&Decl, Field);
}
/* Check if the field itself has a flexible array member */
if (IsClassStruct (Decl.Type)) {
- SymEntry* Sym = GetSymType (Decl.Type);
- if (Sym && SymHasFlexibleArrayMember (Sym)) {
- Entry->Flags |= SC_HAVEFAM;
+ SymEntry* TagEntry = GetESUTagSym (Decl.Type);
+ if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) {
+ Field->Flags |= SC_HAVEFAM;
Flags |= SC_HAVEFAM;
}
}
@@ -992,8 +1026,8 @@ NextMember: if (CurTok.Tok != TOK_COMMA) {
-static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags)
-/* Parse a struct declaration. */
+static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
+/* Parse a struct specifier */
{
unsigned StructSize;
@@ -1002,14 +1036,14 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags)
int FieldWidth; /* Width in bits, -1 if not a bit-field */
SymTable* FieldTab;
SymEntry* StructTagEntry;
- SymEntry* Entry;
+ SymEntry* Field;
unsigned Flags = 0;
unsigned PrevErrorCount = ErrorCount;
if (CurTok.Tok != TOK_LCURLY) {
/* Just a forward declaration */
- return ESUForwardDecl (Name, SC_STRUCT, DSFlags);
+ return ForwardESU (Name, SC_STRUCT, DSFlags);
}
/* Add a forward declaration for the struct tag in the current lexical level */
@@ -1031,6 +1065,7 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags)
/* Get the type of the entry */
DeclSpec Spec;
+ int SignednessSpecified = 0;
/* Check for a _Static_assert */
if (CurTok.Tok == TOK_STATIC_ASSERT) {
@@ -1038,14 +1073,13 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags)
continue;
}
- int SignednessSpecified = 0;
InitDeclSpec (&Spec);
- ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified);
+ ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified);
/* Read fields with this type */
while (1) {
- Declaration Decl;
+ Declarator Decl;
/* If we had a flexible array member before, no other fields can
** follow.
@@ -1147,17 +1181,17 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags)
StructSize += BitOffs / CHAR_BITS;
BitOffs %= CHAR_BITS;
} else if (Decl.Ident[0] != '\0') {
- Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize);
+ Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize);
if (IsAnonName (Decl.Ident)) {
- Entry->V.A.ANumber = StructTagEntry->V.S.ACount++;
- AliasAnonStructFields (&Decl, Entry);
+ Field->V.A.ANumber = StructTagEntry->V.S.ACount++;
+ AliasAnonStructFields (&Decl, Field);
}
/* Check if the field itself has a flexible array member */
if (IsClassStruct (Decl.Type)) {
- SymEntry* Sym = GetSymType (Decl.Type);
- if (Sym && SymHasFlexibleArrayMember (Sym)) {
- Entry->Flags |= SC_HAVEFAM;
+ SymEntry* TagEntry = GetESUTagSym (Decl.Type);
+ if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) {
+ Field->Flags |= SC_HAVEFAM;
Flags |= SC_HAVEFAM;
}
}
@@ -1206,15 +1240,15 @@ NextMember: if (CurTok.Tok != TOK_COMMA) {
-static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
- int* SignednessSpecified)
+static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified)
/* 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.
*/
{
ident Ident;
- SymEntry* Entry;
+ SymEntry* TagEntry;
+ TypeCode Qualifiers = T_QUAL_NONE;
if (SignednessSpecified != NULL) {
*SignednessSpecified = 0;
@@ -1223,8 +1257,8 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
/* Assume we have an explicit type */
D->Flags &= ~DS_DEF_TYPE;
- /* Read type qualifiers if we have any */
- Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE);
+ /* Read storage specifiers and/or type qualifiers if we have any */
+ OptionalSpecifiers (D, &Qualifiers, TSFlags);
/* Look at the data type */
switch (CurTok.Tok) {
@@ -1384,10 +1418,10 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
/* Remember we have an extra type decl */
D->Flags |= DS_EXTRA_TYPE;
/* Declare the union in the current scope */
- Entry = ParseUnionDecl (Ident, &D->Flags);
+ TagEntry = ParseUnionSpec (Ident, &D->Flags);
/* Encode the union entry into the type */
D->Type[0].C = T_UNION;
- SetESUSymEntry (D->Type, Entry);
+ SetESUTagSym (D->Type, TagEntry);
D->Type[1].C = T_END;
break;
@@ -1403,10 +1437,10 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
/* Remember we have an extra type decl */
D->Flags |= DS_EXTRA_TYPE;
/* Declare the struct in the current scope */
- Entry = ParseStructDecl (Ident, &D->Flags);
+ TagEntry = ParseStructSpec (Ident, &D->Flags);
/* Encode the struct entry into the type */
D->Type[0].C = T_STRUCT;
- SetESUSymEntry (D->Type, Entry);
+ SetESUTagSym (D->Type, TagEntry);
D->Type[1].C = T_END;
break;
@@ -1419,17 +1453,16 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
} else {
if (CurTok.Tok != TOK_LCURLY) {
Error ("Identifier expected");
- } else {
- AnonName (Ident, "enum");
}
+ AnonName (Ident, "enum");
}
/* Remember we have an extra type decl */
D->Flags |= DS_EXTRA_TYPE;
/* Parse the enum decl */
- Entry = ParseEnumDecl (Ident, &D->Flags);
+ TagEntry = ParseEnumSpec (Ident, &D->Flags);
/* Encode the enum entry into the type */
D->Type[0].C |= T_ENUM;
- SetESUSymEntry (D->Type, Entry);
+ SetESUTagSym (D->Type, TagEntry);
D->Type[1].C = T_END;
/* 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.
@@ -1442,11 +1475,11 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
case TOK_IDENT:
/* This could be a label */
if (NextTok.Tok != TOK_COLON || GetLexicalLevel () == LEX_LEVEL_STRUCT) {
- Entry = FindSym (CurTok.Ident);
- if (Entry && SymIsTypeDef (Entry)) {
+ TagEntry = FindSym (CurTok.Ident);
+ if (TagEntry && SymIsTypeDef (TagEntry)) {
/* It's a typedef */
NextToken ();
- TypeCopy (D->Type, Entry->Type);
+ TypeCopy (D->Type, TagEntry->Type);
/* If it's a typedef, we should actually use whether the signedness was
** specified on the typedef, but that information has been lost. Treat the
** signedness as being specified to work around the ICE in #1267.
@@ -1471,20 +1504,21 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
/* FALL THROUGH */
default:
- if (Default < 0) {
+ if ((TSFlags & TS_MASK_DEFAULT_TYPE) != TS_DEFAULT_TYPE_INT) {
Error ("Type expected");
D->Type[0].C = T_INT;
D->Type[1].C = T_END;
} else {
D->Flags |= DS_DEF_TYPE;
- D->Type[0].C = (TypeCode) Default;
+ D->Type[0].C = T_INT;
D->Type[1].C = T_END;
}
break;
}
- /* There may also be qualifiers *after* the initial type */
- D->Type[0].C |= (Qualifiers | OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE));
+ /* There may also be specifiers/qualifiers *after* the initial type */
+ OptionalSpecifiers (D, &Qualifiers, TSFlags);
+ D->Type[0].C |= Qualifiers;
}
@@ -1564,7 +1598,7 @@ static void ParseOldStyleParamList (FuncDesc* F)
DeclSpec Spec;
/* Read the declaration specifier */
- ParseDeclSpec (&Spec, SC_AUTO, T_INT);
+ ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
/* We accept only auto and register as storage class specifiers, but
** we ignore all this, since we use auto anyway.
@@ -1577,7 +1611,7 @@ static void ParseOldStyleParamList (FuncDesc* F)
/* Parse a comma separated variable list */
while (1) {
- Declaration Decl;
+ Declarator Decl;
/* Read the parameter */
ParseDecl (&Spec, &Decl, DM_NEED_IDENT);
@@ -1591,19 +1625,19 @@ static void ParseOldStyleParamList (FuncDesc* F)
if (Decl.Ident[0] != '\0') {
/* We have a name given. Search for the symbol */
- SymEntry* Sym = FindLocalSym (Decl.Ident);
- if (Sym) {
+ SymEntry* Param = FindLocalSym (Decl.Ident);
+ if (Param) {
/* Check if we already changed the type for this
** parameter
*/
- if (Sym->Flags & SC_DEFTYPE) {
+ if (Param->Flags & SC_DEFTYPE) {
/* Found it, change the default type to the one given */
- ChangeSymType (Sym, ParamTypeCvt (Decl.Type));
+ SymChangeType (Param, ParamTypeCvt (Decl.Type));
/* Reset the "default type" flag */
- Sym->Flags &= ~SC_DEFTYPE;
+ Param->Flags &= ~SC_DEFTYPE;
} else {
/* Type has already been changed */
- Error ("Redefinition for parameter '%s'", Sym->Name);
+ Error ("Redefinition for parameter '%s'", Param->Name);
}
} else {
Error ("Unknown identifier: '%s'", Decl.Ident);
@@ -1632,8 +1666,8 @@ static void ParseAnsiParamList (FuncDesc* F)
while (CurTok.Tok != TOK_RPAREN) {
DeclSpec Spec;
- Declaration Decl;
- SymEntry* Sym;
+ Declarator Decl;
+ SymEntry* Param;
/* Allow an ellipsis as last parameter */
if (CurTok.Tok == TOK_ELLIPSIS) {
@@ -1643,7 +1677,7 @@ static void ParseAnsiParamList (FuncDesc* F)
}
/* Read the declaration specifier */
- ParseDeclSpec (&Spec, SC_AUTO, T_INT);
+ ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
/* We accept only auto and register as storage class specifiers */
if ((Spec.StorageClass & SC_AUTO) == SC_AUTO) {
@@ -1681,10 +1715,10 @@ static void ParseAnsiParamList (FuncDesc* F)
ParseAttribute (&Decl);
/* Create a symbol table entry */
- Sym = AddLocalSym (Decl.Ident, ParamTypeCvt (Decl.Type), Decl.StorageClass, 0);
+ Param = AddLocalSym (Decl.Ident, ParamTypeCvt (Decl.Type), Decl.StorageClass, 0);
/* Add attributes if we have any */
- SymUseAttr (Sym, &Decl);
+ SymUseAttr (Param, &Decl);
/* If the parameter is a struct or union, emit a warning */
if (IsClassStruct (Decl.Type)) {
@@ -1713,7 +1747,7 @@ static void ParseAnsiParamList (FuncDesc* F)
static FuncDesc* ParseFuncDecl (void)
-/* Parse the argument list of a function. */
+/* Parse the argument list of a function with the enclosing parentheses */
{
SymEntry* Sym;
SymEntry* WrappedCall;
@@ -1725,6 +1759,9 @@ static FuncDesc* ParseFuncDecl (void)
/* Enter a new lexical level */
EnterFunctionLevel ();
+ /* Skip the opening paren */
+ NextToken ();
+
/* Check for several special parameter lists */
if (CurTok.Tok == TOK_RPAREN) {
/* Parameter list is empty (K&R-style) */
@@ -1782,16 +1819,16 @@ static FuncDesc* ParseFuncDecl (void)
-static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
-/* Recursively process declarators. Build a type array in reverse order. */
+static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
+/* Recursively process direct declarators. Build a type array in reverse order. */
{
- /* Read optional function or pointer qualifiers. They modify the
- ** identifier or token to the right. For convenience, we allow a calling
- ** convention also for pointers here. If it's a pointer-to-function, the
- ** qualifier later will be transfered to the function itself. If it's a
- ** pointer to something else, it will be flagged as an error.
+ /* Read optional function or pointer qualifiers that modify the identifier
+ ** or token to the right. For convenience, we allow a calling convention
+ ** also for pointers here. If it's a pointer-to-function, the qualifier
+ ** later will be transfered to the function itself. If it's a pointer to
+ ** something else, it will be flagged as an error.
*/
- TypeCode Qualifiers = OptionalQualifiers (T_QUAL_ADDRSIZE | T_QUAL_CCONV);
+ TypeCode Qualifiers = OptionalQualifiers (T_QUAL_NONE, T_QUAL_ADDRSIZE | T_QUAL_CCONV);
/* Pointer to something */
if (CurTok.Tok == TOK_STAR) {
@@ -1800,19 +1837,19 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
NextToken ();
/* Allow const, restrict, and volatile qualifiers */
- Qualifiers |= OptionalQualifiers (T_QUAL_CVR);
+ Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR);
/* Parse the type that the pointer points to */
- Declarator (Spec, D, Mode);
+ DirectDecl (Spec, D, Mode);
/* Add the type */
- AddTypeToDeclaration (D, T_PTR | Qualifiers);
+ AddTypeCodeToDeclarator (D, T_PTR | Qualifiers);
return;
}
if (CurTok.Tok == TOK_LPAREN) {
NextToken ();
- Declarator (Spec, D, Mode);
+ DirectDecl (Spec, D, Mode);
ConsumeRParen ();
} else {
/* Things depend on Mode now:
@@ -1832,7 +1869,13 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
NextToken ();
} else {
if (Mode == DM_NEED_IDENT) {
+ /* Some fix point tokens that are used for error recovery */
+ static const token_t TokenList[] = { TOK_COMMA, TOK_SEMI };
+
Error ("Identifier expected");
+
+ /* Try some smart error recovery */
+ SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
}
D->Ident[0] = '\0';
}
@@ -1841,14 +1884,11 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) {
if (CurTok.Tok == TOK_LPAREN) {
- /* Function declaration */
+ /* Function declarator */
FuncDesc* F;
SymEntry* PrevEntry;
- /* Skip the opening paren */
- NextToken ();
-
- /* Parse the function declaration */
+ /* Parse the function declarator */
F = ParseFuncDecl ();
/* We cannot specify fastcall for variadic functions */
@@ -1877,7 +1917,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
Qualifiers = T_QUAL_NONE;
} else {
- /* Array declaration. */
+ /* Array declarator */
long Size = UNSPECIFIED;
/* We cannot have any qualifiers for an array */
@@ -1941,11 +1981,11 @@ Type* ParseType (Type* T)
/* Parse a complete type specification */
{
DeclSpec Spec;
- Declaration Decl;
+ Declarator Decl;
/* Get a type without a default */
InitDeclSpec (&Spec);
- ParseTypeSpec (&Spec, -1, T_QUAL_NONE, NULL);
+ ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, NULL);
/* Parse additional declarators */
ParseDecl (&Spec, &Decl, DM_NO_IDENT);
@@ -1959,19 +1999,19 @@ Type* ParseType (Type* T)
-void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
-/* Parse a variable, type or function declaration */
+void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode)
+/* Parse a variable, type or function declarator */
{
/* Used to check if we have any errors during parsing this */
unsigned PrevErrorCount = ErrorCount;
- /* Initialize the Declaration struct */
- InitDeclaration (D);
+ /* Initialize the Declarator struct */
+ InitDeclarator (D);
- /* Get additional declarators and the identifier */
- Declarator (Spec, D, Mode);
+ /* Get additional derivation of the declarator and the identifier */
+ DirectDecl (Spec, D, Mode);
- /* Add the base type. */
+ /* Add the base type */
NeedTypeSpace (D, TypeLen (Spec->Type) + 1); /* Bounds check */
TypeCopy (D->Type + D->Index, Spec->Type);
@@ -1989,7 +2029,7 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
D->StorageClass |= SC_FUNC;
}
- /* Parse attributes for this declaration */
+ /* Parse attributes for this declarator */
ParseAttribute (D);
/* Check several things for function or function pointer types */
@@ -2070,22 +2110,23 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
-void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, long DefType)
+void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage)
/* Parse a declaration specification */
{
- TypeCode Qualifiers;
-
/* Initialize the DeclSpec struct */
InitDeclSpec (D);
- /* There may be qualifiers *before* the storage class specifier */
- Qualifiers = OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE);
+ /* Assume we're using an explicit storage class */
+ D->Flags &= ~DS_DEF_STORAGE;
- /* Now get the storage class specifier for this declaration */
- ParseStorageClass (D, DefStorage);
+ /* Parse the type specifiers */
+ ParseTypeSpec (D, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL);
- /* Parse the type specifiers passing any initial type qualifiers */
- ParseTypeSpec (D, DefType, Qualifiers, NULL);
+ /* If no explicit storage class is given, use the default */
+ if (D->StorageClass == 0) {
+ D->Flags |= DS_DEF_STORAGE;
+ D->StorageClass = DefStorage;
+ }
}
diff --git a/src/cc65/declare.h b/src/cc65/declare.h
index 2b8b36f1c..ee9e1fc63 100644
--- a/src/cc65/declare.h
+++ b/src/cc65/declare.h
@@ -53,6 +53,22 @@
+/* Type specifier parser flags */
+typedef enum typespec_t typespec_t;
+enum typespec_t {
+ TS_NONE = 0x00,
+
+ /* Default type */
+ TS_MASK_DEFAULT_TYPE = 0x03,
+ TS_DEFAULT_TYPE_NONE = 0x00, /* No default type */
+ TS_DEFAULT_TYPE_INT = 0x01, /* Good old int */
+ 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 */
+};
+
/* Masks for the Flags field in DeclSpec */
#define DS_DEF_STORAGE 0x0001U /* Default storage class used */
#define DS_DEF_TYPE 0x0002U /* Default type used */
@@ -70,8 +86,8 @@ struct DeclSpec {
};
/* Result of ParseDecl */
-typedef struct Declaration Declaration;
-struct Declaration {
+typedef struct Declarator Declarator;
+struct Declarator {
unsigned StorageClass; /* A set of SC_xxx flags */
Type Type[MAXTYPELEN]; /* The type */
ident Ident; /* The identifier, if any*/
@@ -102,10 +118,10 @@ void InitDeclSpec (DeclSpec* D);
Type* ParseType (Type* Type);
/* Parse a complete type specification */
-void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode);
-/* Parse a variable, type or function declaration */
+void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode);
+/* Parse a variable, type or function declarator */
-void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, long DefType);
+void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage);
/* Parse a declaration specification */
void CheckEmptyDecl (const DeclSpec* D);
diff --git a/src/cc65/declattr.c b/src/cc65/declattr.c
index 37048e69b..eec89552e 100644
--- a/src/cc65/declattr.c
+++ b/src/cc65/declattr.c
@@ -2,7 +2,7 @@
/* */
/* declattr.c */
/* */
-/* Declaration attributes */
+/* Declarator attributes */
/* */
/* */
/* */
@@ -55,8 +55,8 @@
/* Forwards for attribute handlers */
-static void NoReturnAttr (Declaration* D);
-static void UnusedAttr (Declaration* D);
+static void NoReturnAttr (Declarator* D);
+static void UnusedAttr (Declarator* D);
@@ -64,7 +64,7 @@ static void UnusedAttr (Declaration* D);
typedef struct AttrDesc AttrDesc;
struct AttrDesc {
const char Name[15];
- void (*Handler) (Declaration*);
+ void (*Handler) (Declarator*);
};
static const AttrDesc AttrTable [] = {
{ "__noreturn__", NoReturnAttr },
@@ -141,8 +141,8 @@ static void ErrorSkip (void)
-static void AddAttr (Declaration* D, DeclAttr* A)
-/* Add an attribute to a declaration */
+static void AddAttr (Declarator* D, DeclAttr* A)
+/* Add an attribute to a declarator */
{
/* Allocate the list if necessary, the add the attribute */
if (D->Attributes == 0) {
@@ -159,7 +159,7 @@ static void AddAttr (Declaration* D, DeclAttr* A)
-static void NoReturnAttr (Declaration* D)
+static void NoReturnAttr (Declarator* D)
/* Parse the "noreturn" attribute */
{
/* Add the noreturn attribute */
@@ -168,7 +168,7 @@ static void NoReturnAttr (Declaration* D)
-static void UnusedAttr (Declaration* D)
+static void UnusedAttr (Declarator* D)
/* Parse the "unused" attribute */
{
/* Add the noreturn attribute */
@@ -177,7 +177,7 @@ static void UnusedAttr (Declaration* D)
-void ParseAttribute (Declaration* D)
+void ParseAttribute (Declarator* D)
/* Parse an additional __attribute__ modifier */
{
/* Do we have an attribute? */
diff --git a/src/cc65/declattr.h b/src/cc65/declattr.h
index 63669cee7..930cd71ff 100644
--- a/src/cc65/declattr.h
+++ b/src/cc65/declattr.h
@@ -2,7 +2,7 @@
/* */
/* declattr.h */
/* */
-/* Declaration attributes */
+/* Declarator attributes */
/* */
/* */
/* */
@@ -45,7 +45,7 @@
/* Forward */
-struct Declaration;
+struct Declarator;
/* Supported attribute types */
typedef enum {
@@ -67,7 +67,7 @@ struct DeclAttr {
-void ParseAttribute (struct Declaration* D);
+void ParseAttribute (struct Declarator* D);
/* Parse an additional __attribute__ modifier */
diff --git a/src/cc65/error.c b/src/cc65/error.c
index f0e023969..3f36d9e97 100644
--- a/src/cc65/error.c
+++ b/src/cc65/error.c
@@ -108,6 +108,36 @@ Collection DiagnosticStrBufs;
+/*****************************************************************************/
+/* Helpers */
+/*****************************************************************************/
+
+
+
+static const char* GetDiagnosticFileName (void)
+/* Get the source file name where the diagnostic info refers to */
+{
+ if (CurTok.LI) {
+ return GetInputName (CurTok.LI);
+ } else {
+ return GetCurrentFilename ();
+ }
+}
+
+
+
+static unsigned GetDiagnosticLineNum (void)
+/* Get the source line number where the diagnostic info refers to */
+{
+ if (CurTok.LI) {
+ return GetInputLine (CurTok.LI);
+ } else {
+ return GetCurrentLineNum ();
+ }
+}
+
+
+
/*****************************************************************************/
/* Handling of fatal errors */
/*****************************************************************************/
@@ -119,17 +149,7 @@ void Fatal (const char* Format, ...)
{
va_list ap;
- const char* FileName;
- unsigned LineNum;
- if (CurTok.LI) {
- FileName = GetInputName (CurTok.LI);
- LineNum = GetInputLine (CurTok.LI);
- } else {
- FileName = GetCurrentFile ();
- LineNum = GetCurrentLine ();
- }
-
- fprintf (stderr, "%s:%u: Fatal: ", FileName, LineNum);
+ fprintf (stderr, "%s:%u: Fatal: ", GetDiagnosticFileName (), GetDiagnosticLineNum ());
va_start (ap, Format);
vfprintf (stderr, Format, ap);
@@ -145,22 +165,12 @@ void Fatal (const char* Format, ...)
void Internal (const char* Format, ...)
-/* Print a message about an internal compiler error and die. */
+/* Print a message about an internal compiler error and die */
{
va_list ap;
- const char* FileName;
- unsigned LineNum;
- if (CurTok.LI) {
- FileName = GetInputName (CurTok.LI);
- LineNum = GetInputLine (CurTok.LI);
- } else {
- FileName = GetCurrentFile ();
- LineNum = GetCurrentLine ();
- }
-
fprintf (stderr, "%s:%u: Internal compiler error:\n",
- FileName, LineNum);
+ GetDiagnosticFileName (), GetDiagnosticLineNum ());
va_start (ap, Format);
vfprintf (stderr, Format, ap);
@@ -184,7 +194,7 @@ void Internal (const char* Format, ...)
static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
-/* Print an error message - internal function*/
+/* Print an error message - internal function */
{
fprintf (stderr, "%s:%u: Error: ", Filename, LineNo);
vfprintf (stderr, Msg, ap);
@@ -206,7 +216,7 @@ void Error (const char* Format, ...)
{
va_list ap;
va_start (ap, Format);
- IntError (GetInputName (CurTok.LI), GetInputLine (CurTok.LI), Format, ap);
+ IntError (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
va_end (ap);
}
@@ -224,11 +234,11 @@ void LIError (const LineInfo* LI, const char* Format, ...)
void PPError (const char* Format, ...)
-/* Print an error message. For use within the preprocessor. */
+/* Print an error message. For use within the preprocessor */
{
va_list ap;
va_start (ap, Format);
- IntError (GetCurrentFile(), GetCurrentLine(), Format, ap);
+ IntError (GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
va_end (ap);
}
@@ -241,7 +251,7 @@ void PPError (const char* Format, ...)
static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
-/* Print warning message - internal function. */
+/* Print a warning message - internal function */
{
if (IS_Get (&WarningsAreErrors)) {
@@ -265,11 +275,11 @@ static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg,
void Warning (const char* Format, ...)
-/* Print warning message. */
+/* Print a warning message */
{
va_list ap;
va_start (ap, Format);
- IntWarning (GetInputName (CurTok.LI), GetInputLine (CurTok.LI), Format, ap);
+ IntWarning (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
va_end (ap);
}
@@ -287,11 +297,11 @@ void LIWarning (const LineInfo* LI, const char* Format, ...)
void PPWarning (const char* Format, ...)
-/* Print warning message. For use within the preprocessor. */
+/* Print a warning message. For use within the preprocessor */
{
va_list ap;
va_start (ap, Format);
- IntWarning (GetCurrentFile(), GetCurrentLine(), Format, ap);
+ IntWarning (GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
va_end (ap);
}
@@ -326,6 +336,55 @@ void ListWarnings (FILE* F)
+/*****************************************************************************/
+/* Handling of other infos */
+/*****************************************************************************/
+
+
+
+static void IntNote (const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
+/* Print a note message - internal function */
+{
+ fprintf (stderr, "%s:%u: Note: ", Filename, LineNo);
+ vfprintf (stderr, Msg, ap);
+ fprintf (stderr, "\n");
+}
+
+
+
+void Note (const char* Format, ...)
+/* Print a note message */
+{
+ va_list ap;
+ va_start (ap, Format);
+ IntNote (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
+ va_end (ap);
+}
+
+
+
+void LINote (const LineInfo* LI, const char* Format, ...)
+/* Print a note message with the line info given explicitly */
+{
+ va_list ap;
+ va_start (ap, Format);
+ IntNote (GetInputName (LI), GetInputLine (LI), Format, ap);
+ va_end (ap);
+}
+
+
+
+void PPNote (const char* Format, ...)
+/* Print a note message. For use within the preprocessor */
+{
+ va_list ap;
+ va_start (ap, Format);
+ IntNote (GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
+ va_end (ap);
+}
+
+
+
/*****************************************************************************/
/* Code */
/*****************************************************************************/
diff --git a/src/cc65/error.h b/src/cc65/error.h
index c4420c434..7fcb03467 100644
--- a/src/cc65/error.h
+++ b/src/cc65/error.h
@@ -92,7 +92,7 @@ void Fatal (const char* Format, ...) attribute ((noreturn, format (printf, 1, 2)
/* Print a message about a fatal error and die */
void Internal (const char* Format, ...) attribute ((noreturn, format (printf, 1, 2)));
-/* Print a message about an internal compiler error and die. */
+/* Print a message about an internal compiler error and die */
void Error (const char* Format, ...) attribute ((format (printf, 1, 2)));
/* Print an error message */
@@ -101,16 +101,16 @@ void LIError (const LineInfo* LI, const char* Format, ...) attribute ((format (p
/* Print an error message with the line info given explicitly */
void PPError (const char* Format, ...) attribute ((format (printf, 1, 2)));
-/* Print an error message. For use within the preprocessor. */
+/* Print an error message. For use within the preprocessor */
void Warning (const char* Format, ...) attribute ((format (printf, 1, 2)));
-/* Print warning message. */
+/* Print a warning message */
void LIWarning (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3)));
/* Print a warning message with the line info given explicitly */
void PPWarning (const char* Format, ...) attribute ((format (printf, 1, 2)));
-/* Print warning message. For use within the preprocessor. */
+/* Print a warning message. For use within the preprocessor */
IntStack* FindWarning (const char* Name);
/* Search for a warning in the WarnMap table and return a pointer to the
@@ -120,6 +120,15 @@ IntStack* FindWarning (const char* Name);
void ListWarnings (FILE* F);
/* Print a list of warning types/names to the given file */
+void Note (const char* Format, ...) attribute ((format (printf, 1, 2)));
+/* Print a note message */
+
+void LINote (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3)));
+/* Print a note message with the line info given explicitly */
+
+void PPNote (const char* Format, ...) attribute ((format (printf, 1, 2)));
+/* Print a note message. For use within the preprocessor */
+
void ErrorReport (void);
/* Report errors (called at end of compile) */
diff --git a/src/cc65/expr.c b/src/cc65/expr.c
index afb5e1960..78a4c516a 100644
--- a/src/cc65/expr.c
+++ b/src/cc65/expr.c
@@ -103,6 +103,100 @@ unsigned GlobalModeFlags (const ExprDesc* Expr)
+static unsigned TypeOfBySize (unsigned Size)
+/* Get the code generator replacement type of the object by its size */
+{
+ unsigned NewType;
+ /* If the size is less than or equal to that of a a long, we will copy
+ ** the struct using the primary register, otherwise we use memcpy.
+ */
+ switch (Size) {
+ case 1: NewType = CF_CHAR; break;
+ case 2: NewType = CF_INT; break;
+ case 3: /* FALLTHROUGH */
+ case 4: NewType = CF_LONG; break;
+ default: NewType = CF_NONE; break;
+ }
+
+ return NewType;
+}
+
+
+
+unsigned TypeOf (const Type* T)
+/* Get the code generator base type of the object */
+{
+ unsigned NewType;
+
+ switch (GetUnderlyingTypeCode (T)) {
+
+ case T_SCHAR:
+ return CF_CHAR;
+
+ case T_UCHAR:
+ return CF_CHAR | CF_UNSIGNED;
+
+ case T_SHORT:
+ case T_INT:
+ return CF_INT;
+
+ case T_USHORT:
+ case T_UINT:
+ case T_PTR:
+ case T_ARRAY:
+ return CF_INT | CF_UNSIGNED;
+
+ case T_LONG:
+ return CF_LONG;
+
+ case T_ULONG:
+ return CF_LONG | CF_UNSIGNED;
+
+ case T_FLOAT:
+ case T_DOUBLE:
+ /* These two are identical in the backend */
+ return CF_FLOAT;
+
+ case T_FUNC:
+ /* Treat this as a function pointer */
+ return CF_INT | CF_UNSIGNED;
+
+ case T_STRUCT:
+ case T_UNION:
+ NewType = TypeOfBySize (SizeOf (T));
+ if (NewType != CF_NONE) {
+ return NewType;
+ }
+ /* Address of ... */
+ return CF_INT | CF_UNSIGNED;
+
+ case T_VOID:
+ case T_ENUM:
+ /* Incomplete enum type */
+ Error ("Incomplete type '%s'", GetFullTypeName (T));
+ return CF_INT;
+
+ default:
+ Error ("Illegal type %04lX", T->C);
+ return CF_INT;
+ }
+}
+
+
+
+unsigned FuncTypeOf (const Type* T)
+/* Get the code generator flag for calling the function */
+{
+ if (GetUnderlyingTypeCode (T) == T_FUNC) {
+ return (T->A.F->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC;
+ } else {
+ Error ("Illegal function type %04lX", T->C);
+ return 0;
+ }
+}
+
+
+
void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Call an expression function with checks. */
{
@@ -193,12 +287,15 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush)
-void LimitExprValue (ExprDesc* Expr)
+void LimitExprValue (ExprDesc* Expr, int WarnOverflow)
/* Limit the constant value of the expression to the range of its type */
{
switch (GetUnderlyingTypeCode (Expr->Type)) {
case T_INT:
case T_SHORT:
+ if (WarnOverflow && ((Expr->IVal < -0x8000) || (Expr->IVal > 0x7FFF))) {
+ Warning ("Signed integer constant overflow");
+ }
Expr->IVal = (int16_t)Expr->IVal;
break;
@@ -218,6 +315,9 @@ void LimitExprValue (ExprDesc* Expr)
break;
case T_SCHAR:
+ if (WarnOverflow && ((Expr->IVal < -0x80) || (Expr->IVal > 0x7F))) {
+ Warning ("Signed character constant overflow");
+ }
Expr->IVal = (int8_t)Expr->IVal;
break;
@@ -274,11 +374,10 @@ static unsigned ExprCheckedSizeOf (const Type* T)
/* Specially checked SizeOf() used in 'sizeof' expressions */
{
unsigned Size = SizeOf (T);
- SymEntry* Sym;
if (Size == 0) {
- Sym = GetSymType (T);
- if (Sym == 0 || !SymIsDef (Sym)) {
+ SymEntry* TagSym = GetESUTagSym (T);
+ if (TagSym == 0 || !SymIsDef (TagSym)) {
Error ("Cannot apply 'sizeof' to incomplete type '%s'", GetFullTypeName (T));
}
}
@@ -1296,13 +1395,13 @@ static void Primary (ExprDesc* E)
/* Let's see if this is a C99-style declaration */
DeclSpec Spec;
InitDeclSpec (&Spec);
- ParseDeclSpec (&Spec, -1, T_QUAL_NONE);
+ ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO);
if (Spec.Type->C != T_END) {
Error ("Mixed declarations and code are not supported in cc65");
while (CurTok.Tok != TOK_SEMI) {
- Declaration Decl;
+ Declarator Decl;
/* Parse one declaration */
ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT);
@@ -1822,7 +1921,7 @@ static void UnaryOp (ExprDesc* Expr)
Expr->Type = IntPromotion (Expr->Type);
/* Limit the calculated value to the range of its type */
- LimitExprValue (Expr);
+ LimitExprValue (Expr, 1);
} else {
unsigned Flags;
@@ -2078,6 +2177,10 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */
/* Check for const operands */
if (lconst && rconst) {
+ /* Evaluate the result for operands */
+ unsigned long Val1 = Expr->IVal;
+ unsigned long Val2 = Expr2.IVal;
+
/* Both operands are constant, remove the generated code */
RemoveCode (&Mark1);
@@ -2085,84 +2188,55 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */
Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type);
/* Handle the op differently for signed and unsigned types */
- if (IsSignSigned (Expr->Type)) {
-
- /* Evaluate the result for signed operands */
- signed long Val1 = Expr->IVal;
- signed long Val2 = Expr2.IVal;
- switch (Tok) {
- case TOK_OR:
- Expr->IVal = (Val1 | Val2);
- break;
- case TOK_XOR:
- Expr->IVal = (Val1 ^ Val2);
- break;
- case TOK_AND:
- Expr->IVal = (Val1 & Val2);
- break;
- case TOK_STAR:
- Expr->IVal = (Val1 * Val2);
- break;
- case TOK_DIV:
- if (Val2 == 0) {
- Error ("Division by zero");
- Expr->IVal = 0x7FFFFFFF;
+ switch (Tok) {
+ case TOK_OR:
+ Expr->IVal = (Val1 | Val2);
+ break;
+ case TOK_XOR:
+ Expr->IVal = (Val1 ^ Val2);
+ break;
+ case TOK_AND:
+ Expr->IVal = (Val1 & Val2);
+ break;
+ case TOK_STAR:
+ Expr->IVal = (Val1 * Val2);
+ break;
+ case TOK_DIV:
+ if (Val2 == 0) {
+ if (!ED_IsUneval (Expr)) {
+ Warning ("Division by zero");
+ }
+ Expr->IVal = 0xFFFFFFFF;
+ } else {
+ /* Handle signed and unsigned operands differently */
+ if (IsSignSigned (Expr->Type)) {
+ Expr->IVal = ((long)Val1 / (long)Val2);
} else {
Expr->IVal = (Val1 / Val2);
}
- break;
- case TOK_MOD:
- if (Val2 == 0) {
- Error ("Modulo operation with zero");
- Expr->IVal = 0;
+ }
+ break;
+ case TOK_MOD:
+ if (Val2 == 0) {
+ if (!ED_IsUneval (Expr)) {
+ Warning ("Modulo operation with zero");
+ }
+ Expr->IVal = 0;
+ } else {
+ /* Handle signed and unsigned operands differently */
+ if (IsSignSigned (Expr->Type)) {
+ Expr->IVal = ((long)Val1 % (long)Val2);
} else {
Expr->IVal = (Val1 % Val2);
}
- break;
- default:
- Internal ("hie_internal: got token 0x%X\n", Tok);
- }
- } else {
-
- /* Evaluate the result for unsigned operands */
- unsigned long Val1 = Expr->IVal;
- unsigned long Val2 = Expr2.IVal;
- switch (Tok) {
- case TOK_OR:
- Expr->IVal = (Val1 | Val2);
- break;
- case TOK_XOR:
- Expr->IVal = (Val1 ^ Val2);
- break;
- case TOK_AND:
- Expr->IVal = (Val1 & Val2);
- break;
- case TOK_STAR:
- Expr->IVal = (Val1 * Val2);
- break;
- case TOK_DIV:
- if (Val2 == 0) {
- Error ("Division by zero");
- Expr->IVal = 0xFFFFFFFF;
- } else {
- Expr->IVal = (Val1 / Val2);
- }
- break;
- case TOK_MOD:
- if (Val2 == 0) {
- Error ("Modulo operation with zero");
- Expr->IVal = 0;
- } else {
- Expr->IVal = (Val1 % Val2);
- }
- break;
- default:
- Internal ("hie_internal: got token 0x%X\n", Tok);
- }
+ }
+ break;
+ default:
+ Internal ("hie_internal: got token 0x%X\n", Tok);
}
/* Limit the calculated value to the range of its type */
- LimitExprValue (Expr);
+ LimitExprValue (Expr, 1);
} else if (lconst && (Gen->Flags & GEN_COMM) && !rconst) {
/* If the LHS constant is an int that fits into an unsigned char, change the
@@ -2215,10 +2289,12 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */
/* Second value is constant - check for div */
type |= CF_CONST;
rtype |= CF_CONST;
- if (Tok == TOK_DIV && Expr2.IVal == 0) {
- Error ("Division by zero");
- } else if (Tok == TOK_MOD && Expr2.IVal == 0) {
- Error ("Modulo operation with zero");
+ if (Expr2.IVal == 0 && !ED_IsUneval (Expr)) {
+ if (Tok == TOK_DIV) {
+ Warning ("Division by zero");
+ } else if (Tok == TOK_MOD) {
+ Warning ("Modulo operation with zero");
+ }
}
if ((Gen->Flags & GEN_NOPUSH) != 0) {
RemoveCode (&Mark2);
@@ -2789,7 +2865,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef)
Expr->Type = rhst;
} else {
/* Limit the calculated value to the range of its type */
- LimitExprValue (Expr);
+ LimitExprValue (Expr, 1);
}
/* The result is always an rvalue */
@@ -3260,7 +3336,7 @@ static void parsesub (ExprDesc* Expr)
/* Just adjust the result type */
Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type);
/* And limit the calculated value to the range of it */
- LimitExprValue (Expr);
+ LimitExprValue (Expr, 1);
}
/* The result is always an rvalue */
ED_MarkExprAsRVal (Expr);
@@ -3814,17 +3890,6 @@ static void hieQuest (ExprDesc* Expr)
ED_Init (&Expr3);
Expr3.Flags = Flags;
- NextToken ();
-
- /* Convert non-integer constant to boolean constant, so that we may just
- ** check it in the same way.
- */
- if (ED_IsConstTrue (Expr)) {
- ED_MakeConstBool (Expr, 1);
- } else if (ED_IsConstFalse (Expr)) {
- ED_MakeConstBool (Expr, 0);
- }
-
if (!ConstantCond) {
/* Condition codes not set, request a test */
ED_RequireTest (Expr);
@@ -3836,6 +3901,15 @@ static void hieQuest (ExprDesc* Expr)
FalseLab = GetLocalLabel ();
g_falsejump (CF_NONE, FalseLab);
} else {
+ /* Convert non-integer constant to boolean constant, so that we
+ ** may just check it in an easier way later.
+ */
+ if (ED_IsConstTrue (Expr)) {
+ ED_MakeConstBool (Expr, 1);
+ } else if (ED_IsConstFalse (Expr)) {
+ ED_MakeConstBool (Expr, 0);
+ }
+
/* Constant boolean subexpression could still have deferred inc/dec
** operations, so just flush their side-effects at this sequence point.
*/
@@ -3844,9 +3918,18 @@ static void hieQuest (ExprDesc* Expr)
if (Expr->IVal == 0) {
/* Remember the current code position */
GetCodePos (&SkippedBranch);
+
+ /* Expr2 is unevaluated when the condition is false */
+ Expr2.Flags |= E_EVAL_UNEVAL;
+ } else {
+ /* Expr3 is unevaluated when the condition is true */
+ Expr3.Flags |= E_EVAL_UNEVAL;
}
}
+ /* Skip the question mark */
+ NextToken ();
+
/* Parse second expression. Remember for later if it is a NULL pointer
** expression, then load it into the primary.
*/
@@ -3863,9 +3946,9 @@ static void hieQuest (ExprDesc* Expr)
ED_FinalizeRValLoad (&Expr2);
} else {
- /* Constant boolean subexpression could still have deferred inc/
- ** dec operations, so just flush their side-effects at this
- ** sequence point.
+ /* Constant subexpression could still have deferred inc/dec
+ ** operations, so just flush their side-effects at this sequence
+ ** point.
*/
DoDeferred (SQP_KEEP_NONE, &Expr2);
}
@@ -3878,30 +3961,26 @@ static void hieQuest (ExprDesc* Expr)
/* Jump around the evaluation of the third expression */
TrueLab = GetLocalLabel ();
- ConsumeColon ();
-
g_jump (TrueLab);
/* Jump here if the first expression was false */
g_defcodelabel (FalseLab);
} else {
if (Expr->IVal == 0) {
- /* Expr2 is unevaluated when the condition is false */
- Expr2.Flags |= E_EVAL_UNEVAL;
-
/* Remove the load code of Expr2 */
RemoveCode (&SkippedBranch);
} else {
/* Remember the current code position */
GetCodePos (&SkippedBranch);
}
- ConsumeColon();
}
+ ConsumeColon ();
+
/* Parse third expression. Remember for later if it is a NULL pointer
** expression, then load it into the primary.
*/
- ExprWithCheck (hie1, &Expr3);
+ ExprWithCheck (hieQuest, &Expr3);
Expr3IsNULL = ED_IsNullPtr (&Expr3);
if (!IsTypeVoid (Expr3.Type) &&
ED_YetToLoad (&Expr3) &&
@@ -3914,18 +3993,15 @@ static void hieQuest (ExprDesc* Expr)
ED_FinalizeRValLoad (&Expr3);
} else {
- /* Constant boolean subexpression could still have deferred inc/
- ** dec operations, so just flush their side-effects at this
- ** sequence point.
+ /* Constant subexpression could still have deferred inc/dec
+ ** operations, so just flush their side-effects at this sequence
+ ** point.
*/
DoDeferred (SQP_KEEP_NONE, &Expr3);
}
Expr3.Type = PtrConversion (Expr3.Type);
if (ConstantCond && Expr->IVal != 0) {
- /* Expr3 is unevaluated when the condition is true */
- Expr3.Flags |= E_EVAL_UNEVAL;
-
/* Remove the load code of Expr3 */
RemoveCode (&SkippedBranch);
}
@@ -4030,6 +4106,8 @@ static void hieQuest (ExprDesc* Expr)
} else {
*Expr = Expr3;
}
+ /* The result expression is always an rvalue */
+ ED_MarkExprAsRVal (Expr);
}
/* Setup the target expression */
diff --git a/src/cc65/expr.h b/src/cc65/expr.h
index abdf8ab0d..5644fb82d 100644
--- a/src/cc65/expr.h
+++ b/src/cc65/expr.h
@@ -51,6 +51,12 @@ typedef struct GenDesc {
unsigned GlobalModeFlags (const ExprDesc* Expr);
/* Return the addressing mode flags for the given expression */
+unsigned TypeOf (const Type* T);
+/* Get the code generator base type of the object */
+
+unsigned FuncTypeOf (const Type* T);
+/* Get the code generator flag for calling the function */
+
void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr);
/* Call an expression function with checks. */
@@ -59,7 +65,7 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr);
** generated code.
*/
-void LimitExprValue (ExprDesc* Expr);
+void LimitExprValue (ExprDesc* Expr, int WarnOverflow);
/* Limit the constant value of the expression to the range of its type */
void PushAddr (const ExprDesc* Expr);
diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c
index 7d0ace004..5924ab6cf 100644
--- a/src/cc65/exprdesc.c
+++ b/src/cc65/exprdesc.c
@@ -67,74 +67,9 @@ ExprDesc* ED_Init (ExprDesc* Expr)
-#if !defined(HAVE_INLINE)
-int ED_IsLocQuasiConst (const ExprDesc* Expr)
-/* Return true if the expression is a constant location of some sort or on the
-** stack.
-*/
-{
- return ED_IsLocConst (Expr) || ED_IsLocStack (Expr);
-}
-#endif
-
-
-
-#if !defined(HAVE_INLINE)
-int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr)
-/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */
-{
- return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr);
-}
-#endif
-
-
-
-#if !defined(HAVE_INLINE)
-int ED_IsIndExpr (const ExprDesc* Expr)
-/* Check if the expression is a reference to its value */
-{
- return (Expr->Flags & E_ADDRESS_OF) == 0 && !ED_IsLocNone (Expr);
-}
-#endif
-
-
-
-int ED_YetToLoad (const ExprDesc* Expr)
-/* Check if the expression needs to be loaded somehow. */
-{
- return ED_NeedsPrimary (Expr) ||
- ED_YetToTest (Expr) ||
- (ED_IsLVal (Expr) && IsQualVolatile (Expr->Type));
-}
-
-
-
-void ED_MarkForUneval (ExprDesc* Expr)
-/* Mark the expression as not to be evaluated */
-{
- Expr->Flags = (Expr->Flags & ~E_MASK_EVAL) | E_EVAL_UNEVAL;
-}
-
-
-
-void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End)
-/* Set the code range for this expression */
-{
- Expr->Flags |= E_HAVE_MARKS;
- Expr->Start = *Start;
- Expr->End = *End;
-}
-
-
-
-int ED_CodeRangeIsEmpty (const ExprDesc* Expr)
-/* Return true if no code was output for this expression */
-{
- /* We must have code marks */
- PRECONDITION (Expr->Flags & E_HAVE_MARKS);
-
- return CodeRangeIsEmpty (&Expr->Start, &Expr->End);
-}
+/*****************************************************************************/
+/* Info Extraction */
+/*****************************************************************************/
@@ -214,132 +149,51 @@ int ED_GetStackOffs (const ExprDesc* Expr, int Offs)
-ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type)
-/* Replace Expr with an absolute const with the given value and type */
+/*****************************************************************************/
+/* Predicates */
+/*****************************************************************************/
+
+
+
+#if !defined(HAVE_INLINE)
+int ED_IsLocQuasiConst (const ExprDesc* Expr)
+/* Return true if the expression is a constant location of some sort or on the
+** stack.
+*/
{
- Expr->Type = Type;
- Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE);
- Expr->Name = 0;
- Expr->Sym = 0;
- Expr->IVal = Value;
- memset (&Expr->V, 0, sizeof (Expr->V));
- return Expr;
+ return ED_IsLocConst (Expr) || ED_IsLocStack (Expr);
}
+#endif
-ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value)
-/* Replace Expr with a constant integer expression with the given value */
+#if !defined(HAVE_INLINE)
+int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr)
+/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */
{
- Expr->Type = type_int;
- Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE);
- Expr->Name = 0;
- Expr->Sym = 0;
- Expr->IVal = Value;
- memset (&Expr->V, 0, sizeof (Expr->V));
- return Expr;
+ return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr);
}
+#endif
-ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value)
-/* Replace Expr with a constant boolean expression with the given value */
+#if !defined(HAVE_INLINE)
+int ED_IsIndExpr (const ExprDesc* Expr)
+/* Check if the expression is a reference to its value */
{
- Expr->Sym = 0;
- Expr->Type = type_bool;
- Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE);
- Expr->Name = 0;
- Expr->IVal = Value;
- memset (&Expr->V, 0, sizeof (Expr->V));
- return Expr;
+ return (Expr->Flags & E_ADDRESS_OF) == 0 &&
+ !ED_IsLocNone (Expr) && !ED_IsLocPrimary (Expr);
}
+#endif
-ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr)
-/* Finalize the result of LoadExpr to be an rvalue in the primary register */
+int ED_YetToLoad (const ExprDesc* Expr)
+/* Check if the expression needs to be loaded somehow. */
{
- Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_ADDRESS_OF);
- Expr->Flags &= ~E_CC_SET;
- Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL);
- Expr->Sym = 0;
- Expr->Name = 0;
- Expr->IVal = 0; /* No offset */
- memset (&Expr->V, 0, sizeof (Expr->V));
- return Expr;
-}
-
-
-
-ExprDesc* ED_AddrExpr (ExprDesc* Expr)
-/* Take address of Expr. The result is always an rvalue */
-{
- switch (Expr->Flags & E_MASK_LOC) {
- case E_LOC_NONE:
- Error ("Cannot get the address of a numeric constant");
- break;
-
- case E_LOC_EXPR:
- Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE);
- Expr->Flags |= E_ADDRESS_OF | E_LOC_PRIMARY | E_RTYPE_RVAL;
- break;
-
- default:
- if ((Expr->Flags & E_ADDRESS_OF) == 0) {
- Expr->Flags &= ~E_MASK_RTYPE;
- Expr->Flags |= E_ADDRESS_OF | E_RTYPE_RVAL;
- } else {
- /* Due to the way we handle arrays, this may happen if we take
- ** the address of a pointer to an array element.
- */
- if (!IsTypePtr (Expr->Type)) {
- Error ("Cannot get the address of an address");
- }
- Expr->Flags &= ~E_MASK_RTYPE;
- Expr->Flags |= E_RTYPE_RVAL;
- }
- break;
- }
- return Expr;
-}
-
-
-
-ExprDesc* ED_IndExpr (ExprDesc* Expr)
-/* Dereference Expr */
-{
- switch (Expr->Flags & E_MASK_LOC) {
- case E_LOC_NONE:
- Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE);
- Expr->Flags |= E_LOC_ABS | E_RTYPE_LVAL;
- break;
-
- case E_LOC_PRIMARY:
- Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE);
- Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL;
- break;
-
- default:
- if ((Expr->Flags & E_ADDRESS_OF) != 0) {
- Expr->Flags &= ~(E_MASK_RTYPE | E_ADDRESS_OF);
- Expr->Flags |= E_RTYPE_LVAL;
- } else {
- /* Due to the limitation of LoadExpr, this may happen after we
- ** have loaded the value from a referenced address, in which
- ** case the content in the primary no longer refers to the
- ** original address. We simply mark this as E_LOC_EXPR so that
- ** some info about the original location can be retained.
- ** If it's really meant to dereference a "pointer value", it
- ** should be done in two steps where the pointervalue should
- ** be the manually loaded first before a call into this, and
- ** the offset should be manually cleared somewhere outside.
- */
- Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE);
- Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL;
- }
- break;
- }
- return Expr;
+ return ED_NeedsPrimary (Expr) ||
+ ED_YetToTest (Expr) ||
+ (ED_IsLVal (Expr) && IsQualVolatile (Expr->Type));
}
@@ -427,6 +281,7 @@ int ED_IsQuasiConst (const ExprDesc* Expr)
}
+
int ED_IsConstAddr (const ExprDesc* Expr)
/* Return true if the expression denotes a constant address of some sort. This
** can be the address of a global variable (maybe with offset) or similar.
@@ -472,6 +327,166 @@ int ED_IsBool (const ExprDesc* Expr)
+/*****************************************************************************/
+/* Manipulation */
+/*****************************************************************************/
+
+
+
+ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type)
+/* Replace Expr with an absolute const with the given value and type */
+{
+ Expr->Type = Type;
+ Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE);
+ Expr->Name = 0;
+ Expr->Sym = 0;
+ Expr->IVal = Value;
+ memset (&Expr->V, 0, sizeof (Expr->V));
+ return Expr;
+}
+
+
+
+ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value)
+/* Replace Expr with a constant integer expression with the given value */
+{
+ Expr->Type = type_int;
+ Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE);
+ Expr->Name = 0;
+ Expr->Sym = 0;
+ Expr->IVal = Value;
+ memset (&Expr->V, 0, sizeof (Expr->V));
+ return Expr;
+}
+
+
+
+ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value)
+/* Replace Expr with a constant boolean expression with the given value */
+{
+ Expr->Sym = 0;
+ Expr->Type = type_bool;
+ Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE);
+ Expr->Name = 0;
+ Expr->IVal = Value;
+ memset (&Expr->V, 0, sizeof (Expr->V));
+ return Expr;
+}
+
+
+
+ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr)
+/* Finalize the result of LoadExpr to be an rvalue in the primary register */
+{
+ Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_ADDRESS_OF);
+ Expr->Flags &= ~E_CC_SET;
+ Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL);
+ Expr->Sym = 0;
+ Expr->Name = 0;
+ Expr->IVal = 0; /* No offset */
+ memset (&Expr->V, 0, sizeof (Expr->V));
+ return Expr;
+}
+
+
+
+ExprDesc* ED_AddrExpr (ExprDesc* Expr)
+/* Take address of Expr. The result is always an rvalue */
+{
+ switch (Expr->Flags & E_MASK_LOC) {
+ case E_LOC_NONE:
+ Error ("Cannot get the address of a numeric constant");
+ break;
+
+ case E_LOC_EXPR:
+ Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE);
+ Expr->Flags |= E_LOC_PRIMARY | E_RTYPE_RVAL;
+ break;
+
+ default:
+ if ((Expr->Flags & E_ADDRESS_OF) == 0) {
+ Expr->Flags &= ~E_MASK_RTYPE;
+ Expr->Flags |= E_ADDRESS_OF | E_RTYPE_RVAL;
+ } else {
+ /* Due to the way we handle arrays, this may happen if we take
+ ** the address of a pointer to an array element.
+ */
+ if (!IsTypePtr (Expr->Type)) {
+ Error ("Cannot get the address of an address");
+ }
+ Expr->Flags &= ~E_MASK_RTYPE;
+ Expr->Flags |= E_RTYPE_RVAL;
+ }
+ break;
+ }
+ return Expr;
+}
+
+
+
+ExprDesc* ED_IndExpr (ExprDesc* Expr)
+/* Dereference Expr */
+{
+ switch (Expr->Flags & E_MASK_LOC) {
+ case E_LOC_NONE:
+ Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE);
+ Expr->Flags |= E_LOC_ABS | E_RTYPE_LVAL;
+ break;
+
+ case E_LOC_PRIMARY:
+ Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE);
+ Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL;
+ break;
+
+ default:
+ if ((Expr->Flags & E_ADDRESS_OF) != 0) {
+ Expr->Flags &= ~(E_MASK_RTYPE | E_ADDRESS_OF);
+ Expr->Flags |= E_RTYPE_LVAL;
+ } else {
+ /* Due to the limitation of LoadExpr, this may happen after we
+ ** have loaded the value from a referenced address, in which
+ ** case the content in the primary no longer refers to the
+ ** original address. We simply mark this as E_LOC_EXPR so that
+ ** some info about the original location can be retained.
+ ** If it's really meant to dereference a "pointer value", it
+ ** should be done in two steps where the pointer value should
+ ** be the manually loaded first before a call into this, and
+ ** the offset should be manually cleared somewhere outside.
+ */
+ Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE);
+ Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL;
+ }
+ break;
+ }
+ return Expr;
+}
+
+
+
+void ED_MarkForUneval (ExprDesc* Expr)
+/* Mark the expression as not to be evaluated */
+{
+ Expr->Flags = (Expr->Flags & ~E_MASK_EVAL) | E_EVAL_UNEVAL;
+}
+
+
+
+const Type* ReplaceType (ExprDesc* Expr, const Type* NewType)
+/* Replace the type of Expr by a copy of Newtype and return the old type string */
+{
+ const Type* OldType = Expr->Type;
+ Expr->Type = TypeDup (NewType);
+ return OldType;
+}
+
+
+
+/*****************************************************************************/
+/* Other Helpers */
+/*****************************************************************************/
+
+
+
void PrintExprDesc (FILE* F, ExprDesc* E)
/* Print an ExprDesc */
{
@@ -576,10 +591,21 @@ void PrintExprDesc (FILE* F, ExprDesc* E)
-const Type* ReplaceType (ExprDesc* Expr, const Type* NewType)
-/* Replace the type of Expr by a copy of Newtype and return the old type string */
+void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End)
+/* Set the code range for this expression */
{
- const Type* OldType = Expr->Type;
- Expr->Type = TypeDup (NewType);
- return OldType;
+ Expr->Flags |= E_HAVE_MARKS;
+ Expr->Start = *Start;
+ Expr->End = *End;
+}
+
+
+
+int ED_CodeRangeIsEmpty (const ExprDesc* Expr)
+/* Return true if no code was output for this expression */
+{
+ /* We must have code marks */
+ PRECONDITION (Expr->Flags & E_HAVE_MARKS);
+
+ return CodeRangeIsEmpty (&Expr->Start, &Expr->End);
}
diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h
index a1674a7cc..2ef8b617f 100644
--- a/src/cc65/exprdesc.h
+++ b/src/cc65/exprdesc.h
@@ -227,6 +227,14 @@ struct ExprDesc {
ExprDesc* ED_Init (ExprDesc* Expr);
/* Initialize an ExprDesc */
+
+
+/*****************************************************************************/
+/* Info Extraction */
+/*****************************************************************************/
+
+
+
#if defined(HAVE_INLINE)
INLINE int ED_GetLoc (const ExprDesc* Expr)
/* Return the location flags from the expression */
@@ -237,6 +245,35 @@ INLINE int ED_GetLoc (const ExprDesc* Expr)
# define ED_GetLoc(Expr) ((Expr)->Flags & E_MASK_LOC)
#endif
+#if defined(HAVE_INLINE)
+INLINE int ED_GetNeeds (const ExprDesc* Expr)
+/* Get flags about what the expression needs. */
+{
+ return (Expr->Flags & E_MASK_NEED);
+}
+#else
+# define ED_GetNeeds(Expr) ((Expr)->Flags & E_MASK_NEED)
+#endif
+
+const char* ED_GetLabelName (const ExprDesc* Expr, long Offs);
+/* Return the assembler label name of the given expression. Beware: This
+** function may use a static buffer, so the name may get "lost" on the second
+** call to the function.
+*/
+
+int ED_GetStackOffs (const ExprDesc* Expr, int Offs);
+/* Get the stack offset of an address on the stack in Expr taking into account
+** an additional offset in Offs.
+*/
+
+
+
+/*****************************************************************************/
+/* Predicates */
+/*****************************************************************************/
+
+
+
#if defined(HAVE_INLINE)
INLINE int ED_IsLocNone (const ExprDesc* Expr)
/* Return true if the expression is an absolute value */
@@ -279,7 +316,7 @@ INLINE int ED_IsLocStack (const ExprDesc* Expr)
#if defined(HAVE_INLINE)
INLINE int ED_IsLocPrimary (const ExprDesc* Expr)
-/* Return true if the expression is an expression in the register pseudo variable */
+/* Return true if the expression is an expression in the primary */
{
return (Expr->Flags & E_MASK_LOC) == E_LOC_PRIMARY;
}
@@ -289,7 +326,7 @@ INLINE int ED_IsLocPrimary (const ExprDesc* Expr)
#if defined(HAVE_INLINE)
INLINE int ED_IsLocExpr (const ExprDesc* Expr)
-/* Return true if the expression is an expression in the primary */
+/* Return true if the expression is an expression referenced in the primary */
{
return (Expr->Flags & E_MASK_LOC) == E_LOC_EXPR;
}
@@ -333,33 +370,14 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr);
#endif
#if defined(HAVE_INLINE)
-INLINE void ED_RequireTest (ExprDesc* Expr)
-/* Mark the expression for a test. */
+INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr)
+/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */
{
- Expr->Flags |= E_NEED_TEST;
+ return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr);
}
#else
-# define ED_RequireTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0)
-#endif
-
-#if defined(HAVE_INLINE)
-INLINE void ED_RequireNoTest (ExprDesc* Expr)
-/* Mark the expression not for a test. */
-{
- Expr->Flags &= ~E_NEED_TEST;
-}
-#else
-# define ED_RequireNoTest(Expr) do { (Expr)->Flags &= ~E_NEED_TEST; } while (0)
-#endif
-
-#if defined(HAVE_INLINE)
-INLINE int ED_GetNeeds (const ExprDesc* Expr)
-/* Get flags about what the expression needs. */
-{
- return (Expr->Flags & E_MASK_NEED);
-}
-#else
-# define ED_GetNeeds(Expr) ((Expr)->Flags & E_MASK_NEED)
+int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr);
+/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */
#endif
#if defined(HAVE_INLINE)
@@ -382,27 +400,6 @@ INLINE int ED_NeedsTest (const ExprDesc* Expr)
# define ED_NeedsTest(Expr) (((Expr)->Flags & E_NEED_TEST) != 0)
#endif
-#if defined(HAVE_INLINE)
-INLINE int ED_YetToTest (const ExprDesc* Expr)
-/* Check if the expression needs to be tested but not yet. */
-{
- return ((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST;
-}
-#else
-# define ED_YetToTest(Expr) (((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST)
-#endif
-
-#if defined(HAVE_INLINE)
-INLINE void ED_TestDone (ExprDesc* Expr)
-/* Mark the expression as tested and condition codes set. */
-{
- Expr->Flags |= E_CC_SET;
-}
-#else
-# define ED_TestDone(Expr) \
- do { (Expr)->Flags |= E_CC_SET; } while (0)
-#endif
-
#if defined(HAVE_INLINE)
INLINE int ED_IsTested (const ExprDesc* Expr)
/* Check if the expression has set the condition codes. */
@@ -414,13 +411,13 @@ INLINE int ED_IsTested (const ExprDesc* Expr)
#endif
#if defined(HAVE_INLINE)
-INLINE void ED_MarkAsUntested (ExprDesc* Expr)
-/* Mark the expression as not tested (condition codes not set). */
+INLINE int ED_YetToTest (const ExprDesc* Expr)
+/* Check if the expression needs to be tested but not yet. */
{
- Expr->Flags &= ~E_CC_SET;
+ return ((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST;
}
#else
-# define ED_MarkAsUntested(Expr) do { (Expr)->Flags &= ~E_CC_SET; } while (0)
+# define ED_YetToTest(Expr) (((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST)
#endif
#if defined(HAVE_INLINE)
@@ -448,9 +445,6 @@ INLINE int ED_NeedsConst (const ExprDesc* Expr)
# define ED_NeedsConst(Expr) (((Expr)->Flags & E_EVAL_IMMUTABLE_RESULT) == E_EVAL_IMMUTABLE_RESULT)
#endif
-void ED_MarkForUneval (ExprDesc* Expr);
-/* Mark the expression as not to be evaluated */
-
#if defined(HAVE_INLINE)
INLINE int ED_IsUneval (const ExprDesc* Expr)
/* Check if the expression is not to be evaluated */
@@ -471,27 +465,6 @@ INLINE int ED_MayHaveNoEffect (const ExprDesc* Expr)
# define ED_MayHaveNoEffect(Expr) (((Expr)->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED)
#endif
-#if defined(HAVE_INLINE)
-INLINE void ED_PropagateFrom (ExprDesc* Expr, const ExprDesc* SubExpr)
-/* Propagate viral flags from subexpression */
-{
- Expr->Flags |= SubExpr->Flags & E_MASK_VIRAL;
-}
-#else
-# define ED_PropagateFrom(Expr, SubExpr) (void)((Expr)->Flags |= (SubExpr)->Flags & E_MASK_VIRAL)
-#endif
-
-#if defined(HAVE_INLINE)
-INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr)
-/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */
-{
- return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr);
-}
-#else
-int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr);
-/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */
-#endif
-
#if defined(HAVE_INLINE)
INLINE int ED_IsAddrExpr (const ExprDesc* Expr)
/* Check if the expression is taken address of instead of its value.
@@ -507,42 +480,14 @@ INLINE int ED_IsAddrExpr (const ExprDesc* Expr)
INLINE int ED_IsIndExpr (const ExprDesc* Expr)
/* Check if the expression is a reference to its value */
{
- return (Expr->Flags & E_ADDRESS_OF) == 0 && !ED_IsLocNone (Expr);
+ return (Expr->Flags & E_ADDRESS_OF) == 0 &&
+ !ED_IsLocNone (Expr) && !ED_IsLocPrimary (Expr);
}
#else
int ED_IsIndExpr (const ExprDesc* Expr);
/* Check if the expression is a reference to its value */
#endif
-void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End);
-/* Set the code range for this expression */
-
-int ED_CodeRangeIsEmpty (const ExprDesc* Expr);
-/* Return true if no code was output for this expression */
-
-const char* ED_GetLabelName (const ExprDesc* Expr, long Offs);
-/* Return the assembler label name of the given expression. Beware: This
-** function may use a static buffer, so the name may get "lost" on the second
-** call to the function.
-*/
-
-int ED_GetStackOffs (const ExprDesc* Expr, int Offs);
-/* Get the stack offset of an address on the stack in Expr taking into account
-** an additional offset in Offs.
-*/
-
-ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type);
-/* Replace Expr with an absolute const with the given value and type */
-
-ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value);
-/* Replace Expr with an constant integer with the given value */
-
-ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value);
-/* Replace Expr with a constant boolean expression with the given value */
-
-ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr);
-/* Finalize the result of LoadExpr to be an rvalue in the primary register */
-
#if defined(HAVE_INLINE)
INLINE int ED_IsLVal (const ExprDesc* Expr)
/* Return true if the expression is a reference */
@@ -563,40 +508,6 @@ INLINE int ED_IsRVal (const ExprDesc* Expr)
# define ED_IsRVal(Expr) (((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_RVAL)
#endif
-#if defined(HAVE_INLINE)
-INLINE void ED_MarkExprAsLVal (ExprDesc* Expr)
-/* Mark the expression as an lvalue.
-** HINT: Consider using ED_IndExpr instead of this, unless you know what
-** consequence there will be, as there are both a big part in the code
-** assuming rvalue = const and a big part assuming rvalue = address.
-*/
-{
- Expr->Flags |= E_RTYPE_LVAL;
-}
-#else
-# define ED_MarkExprAsLVal(Expr) do { (Expr)->Flags |= E_RTYPE_LVAL; } while (0)
-#endif
-
-#if defined(HAVE_INLINE)
-INLINE void ED_MarkExprAsRVal (ExprDesc* Expr)
-/* Mark the expression as an rvalue.
-** HINT: Consider using ED_AddrExpr instead of this, unless you know what
-** consequence there will be, as there are both a big part in the code
-** assuming rvalue = const and a big part assuming rvalue = address.
-*/
-{
- Expr->Flags &= ~E_RTYPE_LVAL;
-}
-#else
-# define ED_MarkExprAsRVal(Expr) do { (Expr)->Flags &= ~E_RTYPE_LVAL; } while (0)
-#endif
-
-ExprDesc* ED_AddrExpr (ExprDesc* Expr);
-/* Take address of Expr */
-
-ExprDesc* ED_IndExpr (ExprDesc* Expr);
-/* Dereference Expr */
-
#if defined(HAVE_INLINE)
INLINE int ED_IsAbs (const ExprDesc* Expr)
/* Return true if the expression denotes a numeric value or address. */
@@ -669,14 +580,136 @@ int ED_IsBool (const ExprDesc* Expr);
** be an operand to a compare operation with 0/NULL.
*/
-void PrintExprDesc (FILE* F, ExprDesc* Expr);
-/* Print an ExprDesc */
+
+
+/*****************************************************************************/
+/* Manipulation */
+/*****************************************************************************/
+
+
+
+ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type);
+/* Replace Expr with an absolute const with the given value and type */
+
+ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value);
+/* Replace Expr with an constant integer with the given value */
+
+ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value);
+/* Replace Expr with a constant boolean expression with the given value */
+
+ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr);
+/* Finalize the result of LoadExpr to be an rvalue in the primary register */
+
+#if defined(HAVE_INLINE)
+INLINE void ED_MarkExprAsLVal (ExprDesc* Expr)
+/* Mark the expression as an lvalue.
+** HINT: Consider using ED_IndExpr instead of this, unless you know what
+** consequence there will be, as there are both a big part in the code
+** assuming rvalue = const and a big part assuming rvalue = address.
+*/
+{
+ Expr->Flags |= E_RTYPE_LVAL;
+}
+#else
+# define ED_MarkExprAsLVal(Expr) do { (Expr)->Flags |= E_RTYPE_LVAL; } while (0)
+#endif
+
+#if defined(HAVE_INLINE)
+INLINE void ED_MarkExprAsRVal (ExprDesc* Expr)
+/* Mark the expression as an rvalue.
+** HINT: Consider using ED_AddrExpr instead of this, unless you know what
+** consequence there will be, as there are both a big part in the code
+** assuming rvalue = const and a big part assuming rvalue = address.
+*/
+{
+ Expr->Flags &= ~E_RTYPE_LVAL;
+}
+#else
+# define ED_MarkExprAsRVal(Expr) do { (Expr)->Flags &= ~E_RTYPE_LVAL; } while (0)
+#endif
+
+ExprDesc* ED_AddrExpr (ExprDesc* Expr);
+/* Take address of Expr */
+
+ExprDesc* ED_IndExpr (ExprDesc* Expr);
+/* Dereference Expr */
+
+#if defined(HAVE_INLINE)
+INLINE void ED_RequireTest (ExprDesc* Expr)
+/* Mark the expression for a test. */
+{
+ Expr->Flags |= E_NEED_TEST;
+}
+#else
+# define ED_RequireTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0)
+#endif
+
+#if defined(HAVE_INLINE)
+INLINE void ED_RequireNoTest (ExprDesc* Expr)
+/* Mark the expression not for a test. */
+{
+ Expr->Flags &= ~E_NEED_TEST;
+}
+#else
+# define ED_RequireNoTest(Expr) do { (Expr)->Flags &= ~E_NEED_TEST; } while (0)
+#endif
+
+#if defined(HAVE_INLINE)
+INLINE void ED_TestDone (ExprDesc* Expr)
+/* Mark the expression as tested and condition codes set. */
+{
+ Expr->Flags |= E_CC_SET;
+}
+#else
+# define ED_TestDone(Expr) \
+ do { (Expr)->Flags |= E_CC_SET; } while (0)
+#endif
+
+#if defined(HAVE_INLINE)
+INLINE void ED_MarkAsUntested (ExprDesc* Expr)
+/* Mark the expression as not tested (condition codes not set). */
+{
+ Expr->Flags &= ~E_CC_SET;
+}
+#else
+# define ED_MarkAsUntested(Expr) do { (Expr)->Flags &= ~E_CC_SET; } while (0)
+#endif
+
+void ED_MarkForUneval (ExprDesc* Expr);
+/* Mark the expression as not to be evaluated */
+
+#if defined(HAVE_INLINE)
+INLINE void ED_PropagateFrom (ExprDesc* Expr, const ExprDesc* SubExpr)
+/* Propagate viral flags from subexpression */
+{
+ Expr->Flags |= SubExpr->Flags & E_MASK_VIRAL;
+}
+#else
+# define ED_PropagateFrom(Expr, SubExpr) (void)((Expr)->Flags |= (SubExpr)->Flags & E_MASK_VIRAL)
+#endif
const Type* ReplaceType (ExprDesc* Expr, const Type* NewType);
/* Replace the type of Expr by a copy of Newtype and return the old type string */
+/*****************************************************************************/
+/* Other Helpers */
+/*****************************************************************************/
+
+
+
+void PrintExprDesc (FILE* F, ExprDesc* Expr);
+/* Print an ExprDesc */
+
+void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End);
+/* Set the code range for this expression */
+
+int ED_CodeRangeIsEmpty (const ExprDesc* Expr);
+/* Return true if no code was output for this expression */
+
+
+
/* End of exprdesc.h */
#endif
diff --git a/src/cc65/function.c b/src/cc65/function.c
index 452181af9..737b068a3 100644
--- a/src/cc65/function.c
+++ b/src/cc65/function.c
@@ -42,6 +42,7 @@
#include "asmlabel.h"
#include "codegen.h"
#include "error.h"
+#include "expr.h"
#include "funcdesc.h"
#include "global.h"
#include "litpool.h"
@@ -613,7 +614,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
/* Could we allocate a register? */
if (Reg < 0) {
/* No register available: Convert parameter to auto */
- CvtRegVarToAuto (Param);
+ SymCvtRegVarToAuto (Param);
} else {
/* Remember the register offset */
Param->V.R.RegOffs = Reg;
diff --git a/src/cc65/global.c b/src/cc65/global.c
index 8b9838dc5..b2c3ef0a0 100644
--- a/src/cc65/global.c
+++ b/src/cc65/global.c
@@ -49,6 +49,7 @@ unsigned char DebugInfo = 0; /* Add debug info to the obj */
unsigned char PreprocessOnly = 0; /* Just preprocess the input */
unsigned char DebugOptOutput = 0; /* Output debug stuff */
unsigned RegisterSpace = 6; /* Space available for register vars */
+unsigned AllowNewComments = 0; /* Allow new style comments in C89 mode */
/* Stackable options */
IntStack WritableStrings = INTSTACK(0); /* Literal strings are r/w */
diff --git a/src/cc65/global.h b/src/cc65/global.h
index 266035346..ba7105130 100644
--- a/src/cc65/global.h
+++ b/src/cc65/global.h
@@ -57,6 +57,7 @@ extern unsigned char DebugInfo; /* Add debug info to the obj */
extern unsigned char PreprocessOnly; /* Just preprocess the input */
extern unsigned char DebugOptOutput; /* Output debug stuff */
extern unsigned RegisterSpace; /* Space available for register vars */
+extern unsigned AllowNewComments; /* Allow new style comments in C89 mode */
/* Stackable options */
extern IntStack WritableStrings; /* Literal strings are r/w */
diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c
index 99dacdca9..2b151e59e 100644
--- a/src/cc65/initdata.c
+++ b/src/cc65/initdata.c
@@ -437,7 +437,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers)
static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* Parse initialization of a struct or union. Return the number of data bytes. */
{
- SymEntry* Sym;
+ SymEntry* TagSym;
SymTable* Tab;
StructInitData SI;
int HasCurly = 0;
@@ -452,15 +452,15 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
}
/* Get a pointer to the struct entry from the type */
- Sym = GetESUSymEntry (T);
+ TagSym = GetESUTagSym (T);
/* Get the size of the struct from the symbol table entry */
- SI.Size = Sym->V.S.Size;
+ SI.Size = TagSym->V.S.Size;
/* Check if this struct definition has a field table. If it doesn't, it
** is an incomplete definition.
*/
- Tab = Sym->V.S.SymTab;
+ Tab = TagSym->V.S.SymTab;
if (Tab == 0) {
Error ("Cannot initialize variables with incomplete type");
/* Try error recovery */
@@ -470,7 +470,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
}
/* Get a pointer to the list of symbols */
- Sym = Tab->SymHead;
+ TagSym = Tab->SymHead;
/* Initialize fields */
SI.Offs = 0;
@@ -479,7 +479,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
while (CurTok.Tok != TOK_RCURLY) {
/* Check for excess elements */
- if (Sym == 0) {
+ if (TagSym == 0) {
/* Is there just one trailing comma before a closing curly? */
if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) {
/* Skip comma and exit scope */
@@ -495,7 +495,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
}
/* Check for special members that don't consume the initializer */
- if ((Sym->Flags & SC_ALIAS) == SC_ALIAS) {
+ if ((TagSym->Flags & SC_ALIAS) == SC_ALIAS) {
/* Just skip */
goto NextMember;
}
@@ -503,13 +503,13 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* This may be an anonymous bit-field, in which case it doesn't
** have an initializer.
*/
- if (SymIsBitField (Sym) && (IsAnonName (Sym->Name))) {
+ if (SymIsBitField (TagSym) && (IsAnonName (TagSym->Name))) {
/* Account for the data and output it if we have at least a full
** byte. We may have more if there was storage unit overlap, for
** example two consecutive 7 bit fields. Those would be packed
** into 2 bytes.
*/
- SI.ValBits += Sym->Type->A.B.Width;
+ SI.ValBits += TagSym->Type->A.B.Width;
CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal));
/* TODO: Generalize this so any type can be used. */
CHECK (SI.ValBits <= LONG_BITS);
@@ -526,7 +526,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
SkipComma = 0;
}
- if (SymIsBitField (Sym)) {
+ if (SymIsBitField (TagSym)) {
/* Parse initialization of one field. Bit-fields need a special
** handling.
@@ -537,14 +537,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
unsigned Shift;
/* Calculate the bitmask from the bit-field data */
- unsigned long Mask = shl_l (1UL, Sym->Type->A.B.Width) - 1UL;
+ unsigned long Mask = shl_l (1UL, TagSym->Type->A.B.Width) - 1UL;
/* Safety ... */
- CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs ==
+ CHECK (TagSym->V.Offs * CHAR_BITS + TagSym->Type->A.B.Offs ==
SI.Offs * CHAR_BITS + SI.ValBits);
/* Read the data, check for a constant integer, do a range check */
- Field = ParseScalarInitInternal (IntPromotion (Sym->Type));
+ Field = ParseScalarInitInternal (IntPromotion (TagSym->Type));
if (!ED_IsConstAbsInt (&Field)) {
Error ("Constant initializer expected");
ED_MakeConstAbsInt (&Field, 1);
@@ -554,19 +554,19 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
** any useful bits.
*/
Val = (unsigned long) Field.IVal & Mask;
- if (IsSignUnsigned (Sym->Type)) {
+ if (IsSignUnsigned (TagSym->Type)) {
if (Field.IVal < 0 || (unsigned long) Field.IVal != Val) {
Warning (IsSignUnsigned (Field.Type) ?
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %lu to %lu" :
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %ld to %lu",
- GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type),
- Sym->Type->A.B.Width, Field.IVal, Val);
+ GetFullTypeName (Field.Type), GetFullTypeName (TagSym->Type),
+ TagSym->Type->A.B.Width, Field.IVal, Val);
}
} else {
/* Sign extend back to full width of host long. */
- unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width;
+ unsigned ShiftBits = sizeof (long) * CHAR_BIT - TagSym->Type->A.B.Width;
long RestoredVal = asr_l (asl_l (Val, ShiftBits), ShiftBits);
if (Field.IVal != RestoredVal) {
Warning (IsSignUnsigned (Field.Type) ?
@@ -574,17 +574,17 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
" changes value from %lu to %ld" :
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %ld to %ld",
- GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type),
- Sym->Type->A.B.Width, Field.IVal, RestoredVal);
+ GetFullTypeName (Field.Type), GetFullTypeName (TagSym->Type),
+ TagSym->Type->A.B.Width, Field.IVal, RestoredVal);
}
}
/* Add the value to the currently stored bit-field value */
- Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs;
+ Shift = (TagSym->V.Offs - SI.Offs) * CHAR_BITS + TagSym->Type->A.B.Offs;
SI.BitVal |= (Val << Shift);
/* Account for the data and output any full bytes we have. */
- SI.ValBits += Sym->Type->A.B.Width;
+ SI.ValBits += TagSym->Type->A.B.Width;
/* Make sure unsigned is big enough to hold the value, 32 bits.
** This cannot be more than 32 bits because a 16-bit or 32-bit
** bit-field will always be byte-aligned with padding before it
@@ -602,14 +602,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* Standard member. We should never have stuff from a
** bit-field left because an anonymous member was added
- ** for padding by ParseStructDecl.
+ ** for padding by ParseStructSpec.
*/
CHECK (SI.ValBits == 0);
/* Flexible array members may only be initialized if they are
** the last field (or part of the last struct field).
*/
- SI.Offs += ParseInitInternal (Sym->Type, Braces, AllowFlexibleMembers && Sym->NextSym == 0);
+ SI.Offs += ParseInitInternal (TagSym->Type, Braces, AllowFlexibleMembers && TagSym->NextSym == 0);
}
/* More initializers? */
@@ -624,10 +624,10 @@ NextMember:
/* Next member. For unions, only the first one can be initialized */
if (IsTypeUnion (T)) {
/* Union */
- Sym = 0;
+ TagSym = 0;
} else {
/* Struct */
- Sym = Sym->NextSym;
+ TagSym = TagSym->NextSym;
}
}
diff --git a/src/cc65/input.c b/src/cc65/input.c
index 0e8fc3276..89c471687 100644
--- a/src/cc65/input.c
+++ b/src/cc65/input.c
@@ -67,6 +67,9 @@
/* The current input line */
StrBuf* Line;
+/* The input line to reuse as the next line */
+static StrBuf* CurReusedLine;
+
/* Current and next input character */
char CurC = '\0';
char NextC = '\0';
@@ -103,8 +106,8 @@ static Collection IFiles = STATIC_COLLECTION_INITIALIZER;
/* List of all active files */
static Collection AFiles = STATIC_COLLECTION_INITIALIZER;
-/* Input stack used when preprocessing. */
-static Collection InputStack = STATIC_COLLECTION_INITIALIZER;
+/* Input stack used when preprocessing */
+static Collection* CurrentInputStack;
/* Counter for the __COUNTER__ macro */
static unsigned MainFileCounter;
@@ -394,34 +397,19 @@ static void GetInputChar (void)
** are read by this function.
*/
{
- /* Drop all pushed fragments that don't have data left */
- while (SB_GetIndex (Line) >= SB_GetLen (Line)) {
- /* Cannot read more from this line, check next line on stack if any */
- if (CollCount (&InputStack) == 0) {
- /* This is THE line */
- break;
- }
- FreeStrBuf (Line);
- Line = CollPop (&InputStack);
+ /* Get the next-next character from the line */
+ if (SB_GetIndex (Line) + 1 < SB_GetLen (Line)) {
+ /* CurC and NextC come from this fragment */
+ CurC = SB_AtUnchecked (Line, SB_GetIndex (Line));
+ NextC = SB_AtUnchecked (Line, SB_GetIndex (Line) + 1);
+ } else {
+ /* NextC is '\0' by default */
+ NextC = '\0';
+
+ /* Get CurC from the line */
+ CurC = SB_LookAt (Line, SB_GetIndex (Line));
}
- /* Now get the next characters from the line */
- if (SB_GetIndex (Line) >= SB_GetLen (Line)) {
- CurC = NextC = '\0';
- } else {
- CurC = SB_AtUnchecked (Line, SB_GetIndex (Line));
- if (SB_GetIndex (Line) + 1 < SB_GetLen (Line)) {
- /* NextC comes from this fragment */
- NextC = SB_AtUnchecked (Line, SB_GetIndex (Line) + 1);
- } else {
- /* NextC comes from next fragment */
- if (CollCount (&InputStack) > 0) {
- NextC = ' ';
- } else {
- NextC = '\0';
- }
- }
- }
}
@@ -441,17 +429,41 @@ void NextChar (void)
+Collection* UseInputStack (Collection* InputStack)
+/* Use the provided input stack for incoming input. Return the previously used
+** InputStack.
+*/
+{
+ Collection* OldInputStack = CurrentInputStack;
+
+ CurrentInputStack = InputStack;
+ return OldInputStack;
+}
+
+
+
+void PushLine (StrBuf* L)
+/* Save the current input line and use a new one */
+{
+ PRECONDITION (CurrentInputStack != 0);
+ CollAppend (CurrentInputStack, Line);
+ Line = L;
+ GetInputChar ();
+}
+
+
+
+void ReuseInputLine (void)
+/* Save and reuse the current line as the next line */
+{
+ CurReusedLine = Line;
+}
+
+
+
void ClearLine (void)
/* Clear the current input line */
{
- unsigned I;
-
- /* Remove all pushed fragments from the input stack */
- for (I = 0; I < CollCount (&InputStack); ++I) {
- FreeStrBuf (CollAtUnchecked (&InputStack, I));
- }
- CollDeleteAll (&InputStack);
-
/* Clear the contents of Line */
SB_Clear (Line);
CurC = '\0';
@@ -482,12 +494,47 @@ int NextLine (void)
int C;
AFile* Input;
- /* Clear the current line */
- ClearLine ();
- SB_Clear (Line);
+ /* Overwrite the next input line with the pushed line if there is one */
+ if (CurReusedLine != 0) {
+ /* Use data move to resolve the issue that Line may be impersistent */
+ if (Line != CurReusedLine) {
+ SB_Move (Line, CurReusedLine);
+ }
+ /* Continue with this Line */
+ InitLine (Line);
+ CurReusedLine = 0;
- /* Must have an input file when called */
- if (CollCount(&AFiles) == 0) {
+ return 1;
+ }
+
+ /* If there are pushed input lines, read from them */
+ if (CurrentInputStack != 0 && CollCount (CurrentInputStack) > 0) {
+ /* Drop all pushed fragments that have no data left until one can be
+ ** used as input.
+ */
+ do {
+ /* Use data move to resolve the issue that Line may be impersistent */
+ if (Line != CollLast (CurrentInputStack)) {
+ SB_Move (Line, CollPop (CurrentInputStack));
+ } else {
+ CollPop (CurrentInputStack);
+ }
+ } while (CollCount (CurrentInputStack) > 0 &&
+ SB_GetIndex (Line) >= SB_GetLen (Line));
+
+ if (SB_GetIndex (Line) < SB_GetLen (Line)) {
+ InitLine (Line);
+
+ /* Successive */
+ return 1;
+ }
+ }
+
+ /* Otherwise, clear the current line */
+ ClearLine ();
+
+ /* Must have an input file when going on */
+ if (CollCount (&AFiles) == 0) {
return 0;
}
@@ -531,15 +578,16 @@ int NextLine (void)
SB_Drop (Line, 1);
}
- /* If we don't have a line continuation character at the end,
- ** we're done with this line. Otherwise replace the character
- ** by a newline and continue reading.
+ /* If we don't have a line continuation character at the end, we
+ ** are done with this line. Otherwise just skip the character and
+ ** continue reading.
*/
- if (SB_LookAtLast (Line) == '\\') {
- Line->Buf[Line->Len-1] = '\n';
- } else {
+ if (SB_LookAtLast (Line) != '\\') {
Input->MissingNL = 0;
break;
+ } else {
+ SB_Drop (Line, 1);
+ ContinueLine ();
}
} else if (C != '\0') { /* Ignore embedded NULs */
@@ -605,7 +653,7 @@ const char* GetInputFile (const struct IFile* IF)
-const char* GetCurrentFile (void)
+const char* GetCurrentFilename (void)
/* Return the name of the current input file */
{
unsigned AFileCount = CollCount (&AFiles);
@@ -620,7 +668,7 @@ const char* GetCurrentFile (void)
-unsigned GetCurrentLine (void)
+unsigned GetCurrentLineNum (void)
/* Return the line number in the current input file */
{
unsigned AFileCount = CollCount (&AFiles);
@@ -635,7 +683,7 @@ unsigned GetCurrentLine (void)
-void SetCurrentLine (unsigned LineNum)
+void SetCurrentLineNum (unsigned LineNum)
/* Set the line number in the current input file */
{
unsigned AFileCount = CollCount (&AFiles);
diff --git a/src/cc65/input.h b/src/cc65/input.h
index fb24bbaa8..9457bdf9b 100644
--- a/src/cc65/input.h
+++ b/src/cc65/input.h
@@ -41,6 +41,7 @@
#include
/* common */
+#include "coll.h"
#include "strbuf.h"
@@ -95,6 +96,17 @@ void NextChar (void);
** are read by this function.
*/
+Collection* UseInputStack (Collection* InputStack);
+/* Use the provided input stack for incoming input. Return the previously used
+** InputStack.
+*/
+
+void PushLine (StrBuf* L);
+/* Save the current input line and use a new one */
+
+void ReuseInputLine (void);
+/* Save and reuse the current line as the next line */
+
void ClearLine (void);
/* Clear the current input line */
@@ -116,13 +128,13 @@ int PreprocessNextLine (void);
const char* GetInputFile (const struct IFile* IF);
/* Return a filename from an IFile struct */
-const char* GetCurrentFile (void);
+const char* GetCurrentFilename (void);
/* Return the name of the current input file */
-unsigned GetCurrentLine (void);
+unsigned GetCurrentLineNum (void);
/* Return the line number in the current input file */
-void SetCurrentLine (unsigned LineNum);
+void SetCurrentLineNum (unsigned LineNum);
/* Set the line number in the current input file */
void SetCurrentFilename (const char* Name);
diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c
index 0b75e0a9c..c5ac43f78 100644
--- a/src/cc65/loadexpr.c
+++ b/src/cc65/loadexpr.c
@@ -36,6 +36,7 @@
/* cc65 */
#include "codegen.h"
#include "error.h"
+#include "expr.h"
#include "exprdesc.h"
#include "global.h"
#include "loadexpr.h"
@@ -92,7 +93,6 @@ static void LoadAddress (unsigned Flags, ExprDesc* Expr)
g_leasp (Expr->IVal);
break;
- case E_LOC_PRIMARY:
case E_LOC_EXPR:
if (Expr->IVal != 0) {
/* We have an expression in the primary plus a constant
diff --git a/src/cc65/locals.c b/src/cc65/locals.c
index ad36bded0..68ac00e62 100644
--- a/src/cc65/locals.c
+++ b/src/cc65/locals.c
@@ -97,8 +97,8 @@ static void AllocStorage (unsigned DataLabel, void (*UseSeg) (), unsigned Size)
-static void ParseRegisterDecl (Declaration* Decl, int Reg)
-/* Parse the declaration of a register variable. Reg is the offset of the
+static void ParseRegisterDecl (Declarator* Decl, int Reg)
+/* Parse the declarator of a register variable. Reg is the offset of the
** variable in the register bank.
*/
{
@@ -186,8 +186,8 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg)
-static void ParseAutoDecl (Declaration* Decl)
-/* Parse the declaration of an auto variable. */
+static void ParseAutoDecl (Declarator* Decl)
+/* Parse the declarator of an auto variable. */
{
unsigned Flags;
SymEntry* Sym;
@@ -287,7 +287,7 @@ static void ParseAutoDecl (Declaration* Decl)
** We abuse the Collection somewhat by using it to store line
** numbers.
*/
- CollReplace (&CurrentFunc->LocalsBlockStack, (void *)(size_t)GetCurrentLine (),
+ CollReplace (&CurrentFunc->LocalsBlockStack, (void *)(size_t)GetCurrentLineNum (),
CollCount (&CurrentFunc->LocalsBlockStack) - 1);
} else {
@@ -382,8 +382,8 @@ static void ParseAutoDecl (Declaration* Decl)
-static void ParseStaticDecl (Declaration* Decl)
-/* Parse the declaration of a static variable. */
+static void ParseStaticDecl (Declarator* Decl)
+/* Parse the declarator of a static variable. */
{
unsigned Size;
@@ -441,12 +441,12 @@ static void ParseStaticDecl (Declaration* Decl)
static void ParseOneDecl (const DeclSpec* Spec)
-/* Parse one variable declaration */
+/* Parse one variable declarator. */
{
- Declaration Decl; /* Declaration data structure */
+ Declarator Decl; /* Declarator data structure */
- /* Read the declaration */
+ /* Read the declarator */
ParseDecl (Spec, &Decl, DM_NEED_IDENT);
/* Check if there are any non-extern storage classes set for function
@@ -465,8 +465,8 @@ static void ParseOneDecl (const DeclSpec* Spec)
/* The default storage class could be wrong. Just clear them */
Decl.StorageClass &= ~SC_STORAGEMASK;
- /* This is always a declaration */
- Decl.StorageClass |= SC_DECL;
+ /* This is always an extern declaration */
+ Decl.StorageClass |= SC_DECL | SC_EXTERN;
}
/* If we don't have a name, this was flagged as an error earlier.
@@ -524,7 +524,9 @@ static void ParseOneDecl (const DeclSpec* Spec)
if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN ||
(Decl.StorageClass & SC_FUNC) == SC_FUNC) {
- /* Add the global symbol to the local symbol table */
+ /* Add the global symbol to both of the global and local symbol
+ ** tables.
+ */
AddGlobalSym (Decl.Ident, Decl.Type, Decl.StorageClass);
} else {
/* Add the local symbol to the local symbol table */
@@ -566,7 +568,7 @@ void DeclareLocals (void)
continue;
}
- ParseDeclSpec (&Spec, SC_AUTO, T_INT);
+ 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 */
diff --git a/src/cc65/macrotab.c b/src/cc65/macrotab.c
index 0e80cd638..3bfae0811 100644
--- a/src/cc65/macrotab.c
+++ b/src/cc65/macrotab.c
@@ -74,19 +74,17 @@ Macro* NewMacro (const char* Name)
*/
{
/* Get the length of the macro name */
- unsigned Len = strlen(Name);
+ unsigned Len = strlen (Name);
/* Allocate the structure */
Macro* M = (Macro*) xmalloc (sizeof(Macro) + Len);
/* Initialize the data */
- M->Next = 0;
- M->Expanding = 0;
- M->ArgCount = -1; /* Flag: Not a function like macro */
- M->MaxArgs = 0;
- InitCollection (&M->FormalArgs);
+ M->Next = 0;
+ M->ParamCount = -1; /* Flag: Not a function-like macro */
+ InitCollection (&M->Params);
SB_Init (&M->Replacement);
- M->Variadic = 0;
+ M->Variadic = 0;
memcpy (M->Name, Name, Len+1);
/* Return the new macro */
@@ -102,10 +100,10 @@ void FreeMacro (Macro* M)
{
unsigned I;
- for (I = 0; I < CollCount (&M->FormalArgs); ++I) {
- xfree (CollAtUnchecked (&M->FormalArgs, I));
+ for (I = 0; I < CollCount (&M->Params); ++I) {
+ xfree (CollAtUnchecked (&M->Params, I));
}
- DoneCollection (&M->FormalArgs);
+ DoneCollection (&M->Params);
SB_Done (&M->Replacement);
xfree (M);
}
@@ -121,12 +119,12 @@ Macro* CloneMacro (const Macro* M)
Macro* New = NewMacro (M->Name);
unsigned I;
- for (I = 0; I < CollCount (&M->FormalArgs); ++I) {
- /* Copy the argument */
- const char* Arg = CollAtUnchecked (&M->FormalArgs, I);
- CollAppend (&New->FormalArgs, xstrdup (Arg));
+ for (I = 0; I < CollCount (&M->Params); ++I) {
+ /* Copy the parameter */
+ const char* Param = CollAtUnchecked (&M->Params, I);
+ CollAppend (&New->Params, xstrdup (Param));
}
- New->ArgCount = M->ArgCount;
+ New->ParamCount = M->ParamCount;
New->Variadic = M->Variadic;
SB_Copy (&New->Replacement, &M->Replacement);
@@ -265,14 +263,14 @@ Macro* FindMacro (const char* Name)
-int FindMacroArg (Macro* M, const char* Arg)
-/* Search for a formal macro argument. If found, return the index of the
-** argument. If the argument was not found, return -1.
+int FindMacroParam (const Macro* M, const char* Param)
+/* Search for a macro parameter. If found, return the index of the parameter.
+** If the parameter was not found, return -1.
*/
{
unsigned I;
- for (I = 0; I < CollCount (&M->FormalArgs); ++I) {
- if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) {
+ for (I = 0; I < CollCount (&M->Params); ++I) {
+ if (strcmp (CollAtUnchecked (&M->Params, I), Param) == 0) {
/* Found */
return I;
}
@@ -284,25 +282,25 @@ int FindMacroArg (Macro* M, const char* Arg)
-void AddMacroArg (Macro* M, const char* Arg)
-/* Add a formal macro argument. */
+void AddMacroParam (Macro* M, const char* Param)
+/* Add a macro parameter. */
{
- /* Check if we have a duplicate macro argument, but add it anyway.
- ** Beware: Don't use FindMacroArg here, since the actual argument array
+ /* Check if we have a duplicate macro parameter, but add it anyway.
+ ** Beware: Don't use FindMacroParam here, since the actual argument array
** may not be initialized.
*/
unsigned I;
- for (I = 0; I < CollCount (&M->FormalArgs); ++I) {
- if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) {
+ for (I = 0; I < CollCount (&M->Params); ++I) {
+ if (strcmp (CollAtUnchecked (&M->Params, I), Param) == 0) {
/* Found */
- PPError ("Duplicate macro parameter: '%s'", Arg);
+ PPError ("Duplicate macro parameter: '%s'", Param);
break;
}
}
- /* Add the new argument */
- CollAppend (&M->FormalArgs, xstrdup (Arg));
- ++M->ArgCount;
+ /* Add the new parameter */
+ CollAppend (&M->Params, xstrdup (Param));
+ ++M->ParamCount;
}
@@ -313,14 +311,14 @@ int MacroCmp (const Macro* M1, const Macro* M2)
int I;
/* Argument count must be identical */
- if (M1->ArgCount != M2->ArgCount) {
+ if (M1->ParamCount != M2->ParamCount) {
return 1;
}
- /* Compare the arguments */
- for (I = 0; I < M1->ArgCount; ++I) {
- if (strcmp (CollConstAt (&M1->FormalArgs, I),
- CollConstAt (&M2->FormalArgs, I)) != 0) {
+ /* Compare the parameters */
+ for (I = 0; I < M1->ParamCount; ++I) {
+ if (strcmp (CollConstAt (&M1->Params, I),
+ CollConstAt (&M2->Params, I)) != 0) {
return 1;
}
}
diff --git a/src/cc65/macrotab.h b/src/cc65/macrotab.h
index 6a09d7281..52b812b2f 100644
--- a/src/cc65/macrotab.h
+++ b/src/cc65/macrotab.h
@@ -55,10 +55,8 @@
typedef struct Macro Macro;
struct Macro {
Macro* Next; /* Next macro with same hash value */
- int Expanding; /* Are we currently expanding this macro? */
- int ArgCount; /* Number of parameters, -1 = no parens */
- unsigned MaxArgs; /* Size of formal argument list */
- Collection FormalArgs; /* Formal argument list (char*) */
+ int ParamCount; /* Number of parameters, -1 = no parens */
+ Collection Params; /* Parameter list (char*) */
StrBuf Replacement; /* Replacement text */
unsigned char Variadic; /* C99 variadic macro */
char Name[1]; /* Name, dynamically allocated */
@@ -120,13 +118,13 @@ INLINE int IsMacro (const char* Name)
# define IsMacro(Name) (FindMacro (Name) != 0)
#endif
-int FindMacroArg (Macro* M, const char* Arg);
-/* Search for a formal macro argument. If found, return the index of the
-** argument. If the argument was not found, return -1.
+int FindMacroParam (const Macro* M, const char* Param);
+/* Search for a macro parameter. If found, return the index of the parameter.
+** If the parameter was not found, return -1.
*/
-void AddMacroArg (Macro* M, const char* Arg);
-/* Add a formal macro argument. */
+void AddMacroParam (Macro* M, const char* Param);
+/* Add a macro parameter. */
int MacroCmp (const Macro* M1, const Macro* M2);
/* Compare two macros and return zero if both are identical. */
diff --git a/src/cc65/ppexpr.c b/src/cc65/ppexpr.c
index dd129ced9..8d8c0b65d 100644
--- a/src/cc65/ppexpr.c
+++ b/src/cc65/ppexpr.c
@@ -305,97 +305,60 @@ static void PPhie_internal (const token_t* Ops, /* List of generators */
if (PPEvaluationEnabled && !PPEvaluationFailed) {
+ /* Evaluate the result for operands */
+ unsigned long Val1 = Expr->IVal;
+ unsigned long Val2 = Rhs.IVal;
+
/* If either side is unsigned, the result is unsigned */
Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED;
- /* Handle the op differently for signed and unsigned integers */
- if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) {
-
- /* Evaluate the result for signed operands */
- signed long Val1 = Expr->IVal;
- signed long Val2 = Rhs.IVal;
- switch (Tok) {
- case TOK_OR:
- Expr->IVal = (Val1 | Val2);
- break;
- case TOK_XOR:
- Expr->IVal = (Val1 ^ Val2);
- break;
- case TOK_AND:
- Expr->IVal = (Val1 & Val2);
- break;
- case TOK_PLUS:
- Expr->IVal = (Val1 + Val2);
- break;
- case TOK_MINUS:
- Expr->IVal = (Val1 - Val2);
- break;
- case TOK_MUL:
- Expr->IVal = (Val1 * Val2);
- break;
- case TOK_DIV:
- if (Val2 == 0) {
- PPError ("Division by zero");
- Expr->IVal = 0;
+ switch (Tok) {
+ case TOK_OR:
+ Expr->IVal = (Val1 | Val2);
+ break;
+ case TOK_XOR:
+ Expr->IVal = (Val1 ^ Val2);
+ break;
+ case TOK_AND:
+ Expr->IVal = (Val1 & Val2);
+ break;
+ case TOK_PLUS:
+ Expr->IVal = (Val1 + Val2);
+ break;
+ case TOK_MINUS:
+ Expr->IVal = (Val1 - Val2);
+ break;
+ case TOK_MUL:
+ Expr->IVal = (Val1 * Val2);
+ break;
+ case TOK_DIV:
+ if (Val2 == 0) {
+ PPError ("Division by zero");
+ Expr->IVal = 0;
+ } else {
+ /* Handle signed and unsigned operands differently */
+ if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) {
+ Expr->IVal = ((long)Val1 / (long)Val2);
} else {
Expr->IVal = (Val1 / Val2);
}
- break;
- case TOK_MOD:
- if (Val2 == 0) {
- PPError ("Modulo operation with zero");
- Expr->IVal = 0;
+ }
+ break;
+ case TOK_MOD:
+ if (Val2 == 0) {
+ PPError ("Modulo operation with zero");
+ Expr->IVal = 0;
+ } else {
+ /* Handle signed and unsigned operands differently */
+ if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) {
+ Expr->IVal = ((long)Val1 % (long)Val2);
} else {
Expr->IVal = (Val1 % Val2);
}
- break;
- default:
- Internal ("PPhie_internal: got token 0x%X\n", Tok);
- }
-
- } else {
-
- /* Evaluate the result for unsigned operands */
- unsigned long Val1 = Expr->IVal;
- unsigned long Val2 = Rhs.IVal;
- switch (Tok) {
- case TOK_OR:
- Expr->IVal = (Val1 | Val2);
- break;
- case TOK_XOR:
- Expr->IVal = (Val1 ^ Val2);
- break;
- case TOK_AND:
- Expr->IVal = (Val1 & Val2);
- break;
- case TOK_PLUS:
- Expr->IVal = (Val1 + Val2);
- break;
- case TOK_MINUS:
- Expr->IVal = (Val1 - Val2);
- break;
- case TOK_MUL:
- Expr->IVal = (Val1 * Val2);
- break;
- case TOK_DIV:
- if (Val2 == 0) {
- PPError ("Division by zero");
- Expr->IVal = 0;
- } else {
- Expr->IVal = (Val1 / Val2);
- }
- break;
- case TOK_MOD:
- if (Val2 == 0) {
- PPError ("Modulo operation with zero");
- Expr->IVal = 0;
- } else {
- Expr->IVal = (Val1 % Val2);
- }
- break;
- default:
- Internal ("PPhie_internal: got token 0x%X\n", Tok);
- }
+ }
+ break;
+ default:
+ Internal ("PPhie_internal: got token 0x%X\n", Tok);
}
}
}
@@ -519,12 +482,21 @@ static void PPhie7 (PPExpr* Expr)
/* Evaluate */
if (PPEvaluationEnabled && !PPEvaluationFailed) {
- /* To shift by a negative value is equivalent to shift to the
- ** opposite direction.
- */
- if ((Rhs.Flags & PPEXPR_UNSIGNED) != 0 && Rhs.IVal > (long)LONG_BITS) {
+ /* For now we use 32-bit integer types for PP integer constants */
+ if ((Rhs.Flags & PPEXPR_UNSIGNED) != 0) {
+ if ((unsigned long)Rhs.IVal > LONG_BITS) {
+ Rhs.IVal = (long)LONG_BITS;
+ }
+ } else if (Rhs.IVal > (long)LONG_BITS) {
Rhs.IVal = (long)LONG_BITS;
+ } else if (Rhs.IVal < -(long)LONG_BITS) {
+ Rhs.IVal = -(long)LONG_BITS;
}
+
+ /* Positive count for left-shift and negative for right-shift. So
+ ** to shift by a count is equivalent to shift to the opposite
+ ** direction by the negated count.
+ */
if (Op == TOK_SHR) {
Rhs.IVal = -Rhs.IVal;
}
@@ -532,27 +504,26 @@ static void PPhie7 (PPExpr* Expr)
/* Evaluate the result */
if ((Expr->Flags & PPEXPR_UNSIGNED) != 0) {
if (Rhs.IVal >= (long)LONG_BITS) {
- /* For now we use (unsigned) long types for integer constants */
PPWarning ("Integer overflow in preprocessor expression");
Expr->IVal = 0;
} else if (Rhs.IVal > 0) {
Expr->IVal <<= Rhs.IVal;
- } else if (Rhs.IVal < -(long)LONG_BITS) {
+ } else if (Rhs.IVal <= -(long)LONG_BITS) {
Expr->IVal = 0;
} else if (Rhs.IVal < 0) {
Expr->IVal = (unsigned long)Expr->IVal >> -Rhs.IVal;
}
} else {
+ /* -1 for sign bit */
if (Rhs.IVal >= (long)(LONG_BITS - 1)) {
- /* For now we use (unsigned) long types for integer constants */
PPWarning ("Integer overflow in preprocessor expression");
Expr->IVal = 0;
} else if (Rhs.IVal > 0) {
Expr->IVal <<= Rhs.IVal;
- } else if (Rhs.IVal < -(long)LONG_BITS) {
- Expr->IVal = -1;
+ } else if (Rhs.IVal <= -(long)LONG_BITS) {
+ Expr->IVal = Expr->IVal >= 0 ? 0 : -1;
} else if (Rhs.IVal < 0) {
- Expr->IVal >>= Expr->IVal >> -Rhs.IVal;
+ Expr->IVal = (long)Expr->IVal >> -Rhs.IVal;
}
}
}
@@ -752,7 +723,7 @@ static void PPhieQuest (PPExpr* Expr)
/* Parse third expression */
PPExprInit (&Expr3);
- PPhie1 (&Expr3);
+ PPhieQuest (&Expr3);
/* Set the result */
Expr->IVal = Expr->IVal ? Expr2.IVal != 0 : Expr3.IVal != 0;
diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c
index b0478ce2a..83ed362c8 100644
--- a/src/cc65/pragma.c
+++ b/src/cc65/pragma.c
@@ -784,7 +784,7 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
static void MakeMessage (const char* Message)
{
- fprintf (stderr, "%s:%u: Note: %s\n", GetInputName (CurTok.LI), GetInputLine (CurTok.LI), Message);
+ Note ("%s", Message);
}
diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c
index 020df011a..0a9b94bf2 100644
--- a/src/cc65/preproc.c
+++ b/src/cc65/preproc.c
@@ -74,11 +74,20 @@
#define MSM_IN_DIRECTIVE 0x02U /* In PP directives scan */
#define MSM_IN_ARG_LIST 0x04U /* In macro argument scan */
#define MSM_IN_ARG_EXPANSION 0x08U /* In expansion on arguments */
-#define MSM_OP_DEFINED 0x10U /* Handle the defined operator */
-#define MSM_OP_HAS_INCLUDE 0x20U /* Handle the __has_include operator */
-#define MSM_OP_HAS_C_ATTRIBUTE 0x40U /* Handle the __has_c_attribute operator */
+#define MSM_OP_DEFINED 0x10U /* Handle the "defined" operator */
+#define MSM_OP_HAS_INCLUDE 0x20U /* Handle the "__has_include" operator */
+#define MSM_OP_HAS_C_ATTRIBUTE 0x40U /* Handle the "__has_c_attribute" operator */
#define MSM_TOK_HEADER 0x80U /* Support header tokens */
+/* Macro expansion state flags */
+#define MES_NONE 0x00U /* Nothing */
+#define MES_FIRST_TOKEN 0x01U /* Mark for detecting pp-token count in the sequence */
+#define MES_MULTIPLE_TOKEN 0x02U /* Multiple pp-tokens are detected in the sequence */
+#define MES_BEGIN_WITH_IDENT 0x04U /* The first pp-token of the sequence is an identifier */
+#define MES_HAS_REPLACEMENT 0x10U /* Macro argument has cached replacement result */
+#define MES_NO_VA_COMMA 0x20U /* Variadic macro called w/o the ',' in front of variable argument */
+#define MES_ERROR 0x80U /* Error has occurred in macro expansion */
+
/* Management data for #if */
#define IFCOND_NONE 0x00U
#define IFCOND_SKIP 0x01U
@@ -88,20 +97,52 @@
/* Current PP if stack */
static PPIfStack* PPStack;
+/* Struct for rescan */
+typedef struct RescanInputStack RescanInputStack;
+struct RescanInputStack {
+ Collection Lines;
+ Collection LastTokLens;
+ StrBuf* PrevTok;
+};
+
+/* Input backup for rescan */
+static RescanInputStack* CurRescanStack;
+
/* Intermediate input buffers */
static StrBuf* PLine; /* Buffer for macro expansion */
static StrBuf* MLine; /* Buffer for macro expansion in #pragma */
static StrBuf* OLine; /* Buffer for #pragma output */
/* Newlines to be added to preprocessed text */
-static int PendingNewLines;
+static unsigned PendingNewLines;
+static unsigned ContinuedLines;
static int FileChanged;
/* Structure used when expanding macros */
typedef struct MacroExp MacroExp;
struct MacroExp {
- Collection ActualArgs; /* Actual arguments */
- StrBuf Replacement; /* Replacement with arguments substituted */
+ Collection Args; /* Actual arguments (for function-like) */
+ Collection HideSets; /* Macros hidden from expansion */
+ StrBuf Tokens; /* Originally read sequence */
+ unsigned IdentCount; /* Count of identifiers in the pp-token sequence */
+ unsigned Flags; /* Macro argument flags */
+ MacroExp* Replaced; /* Macro-replaced version of this pp-token sequence */
+ unsigned FirstTokLen; /* Length of the first pp-token */
+ unsigned LastTokLen; /* Length of the last pp-token */
+};
+
+typedef struct HideRange HideRange;
+struct HideRange
+{
+ HideRange* Next;
+ unsigned Start;
+ unsigned End;
+};
+
+typedef struct HiddenMacro HiddenMacro;
+struct HiddenMacro {
+ const Macro* M;
+ HideRange* HS;
};
@@ -125,8 +166,26 @@ static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFl
static int ParseDirectives (unsigned ModeFlags);
/* Handle directives. Return 1 if any whitespace or newlines are parsed. */
-static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags);
-/* Scan for and perform macro replacement */
+static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsigned ModeFlags);
+/* Scan for and perform macro replacement. Return the count of identifiers and
+** right parentheses in the replacement result.
+*/
+
+static MacroExp* InitMacroExp (MacroExp* E);
+/* Initialize a MacroExp structure */
+
+static void DoneMacroExp (MacroExp* E);
+/* Cleanup after use of a MacroExp structure */
+
+static int CheckPastePPTok (StrBuf* Target, unsigned TokLen, char Next);
+/* Return 1 if the last pp-tokens from Source could be concatenated with any
+** characters from Appended to form a new valid one.
+*/
+
+static void LazyCheckNextPPTok (const StrBuf* Prev, unsigned LastTokLen);
+/* Memorize the previous pp-token(s) to later check for potential pp-token
+** concatenation.
+*/
@@ -201,17 +260,451 @@ static ppdirective_t FindPPDirectiveType (const char* Ident)
+/*****************************************************************************/
+/* MacroExp helpers */
+/*****************************************************************************/
+
+
+
+static HideRange* NewHideRange (unsigned Start, unsigned Len)
+/* Create a hide range */
+{
+ HideRange* HS = xmalloc (sizeof (HideRange));
+
+ HS->Next = 0;
+ HS->Start = Start;
+ HS->End = Start + Len;
+
+ return HS;
+}
+
+
+
+static void FreeHideRange (HideRange* HS)
+/* Free a hide range */
+{
+ xfree (HS);
+}
+
+
+
+static HiddenMacro* NewHiddenMacro (const Macro* M)
+/* Create a new struct for the hidden macro */
+{
+ HiddenMacro* MHS = xmalloc (sizeof (HiddenMacro));
+
+ MHS->M = M;
+ MHS->HS = 0;
+
+ return MHS;
+}
+
+
+
+static void FreeHiddenMacro (HiddenMacro* MHS)
+/* Free the struct and all ranges of the hidden macro */
+{
+ HideRange* This;
+ HideRange* Next;
+
+ if (MHS == 0) {
+ return;
+ }
+
+ for (This = MHS->HS; This != 0; This = Next) {
+ Next = This->Next;
+ FreeHideRange (This);
+ }
+
+ xfree (MHS);
+}
+
+
+
/*****************************************************************************/
/* struct MacroExp */
/*****************************************************************************/
+static HiddenMacro* ME_FindHiddenMacro (const MacroExp* E, const Macro* M)
+/* Find the macro hide set */
+{
+ unsigned I;
+
+ for (I = 0; I < CollCount (&E->HideSets); ++I) {
+ HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I);
+ if (MHS->M == M) {
+ return MHS;
+ }
+ }
+
+ return 0;
+}
+
+
+
+static void ME_HideMacro (unsigned Idx, unsigned Count, MacroExp* E, const Macro* M)
+/* Hide the macro from the Idx'th identifier */
+{
+ if (Count > 0) {
+ /* Find the macro hideset */
+ HiddenMacro* MHS = ME_FindHiddenMacro (E, M);
+ HideRange** This;
+
+ /* New hidden section */
+ HideRange* NewHS = NewHideRange (Idx, Count);
+
+ /* New macro to hide */
+ if (MHS == 0) {
+ MHS = NewHiddenMacro (M);
+ CollAppend (&E->HideSets, MHS);
+ }
+ This = &MHS->HS;
+
+ if (*This == 0) {
+ *This = NewHS;
+ } else {
+ /* Insert */
+ while (1) {
+ if (*This == 0 || NewHS->Start <= (*This)->Start) {
+ /* Insert before */
+ NewHS->Next = *This;
+ *This = NewHS;
+ break;
+ } else if (NewHS->Start <= (*This)->End) {
+ /* Insert after */
+ NewHS->Next = (*This)->Next;
+ (*This)->Next = NewHS;
+ break;
+ }
+ /* Advance */
+ This = &(*This)->Next;
+ }
+
+ /* Merge */
+ while (*This != 0) {
+ HideRange* Next = (*This)->Next;
+
+ if (Next != 0 && (*This)->End >= Next->Start) {
+ /* Expand this to the next */
+ if ((*This)->End < Next->End) {
+ (*This)->End = Next->End;
+ }
+
+ /* Remove next */
+ (*This)->Next = Next->Next;
+ FreeHideRange (Next);
+
+ /* Advance */
+ This = &(*This)->Next;
+ } else {
+ /* No more */
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+
+static int ME_CanExpand (unsigned Idx, const MacroExp* E, const Macro* M)
+/* Return 1 if the macro can be expanded with the Idx'th identifier */
+{
+ if (E != 0) {
+ /* Find the macro hideset */
+ HiddenMacro* MHS = ME_FindHiddenMacro (E, M);
+ if (MHS != 0) {
+ /* Check if the macro is hidden from this identifier */
+ HideRange* HS = MHS->HS;
+ while (HS != 0) {
+ /* If the macro name overlaps with the range where the macro is hidden,
+ ** the macro cannot be expanded.
+ */
+ if (Idx >= HS->Start && Idx < HS->End) {
+ return 0;
+ }
+ HS = HS->Next;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+
+static void ME_OffsetHideSets (unsigned Idx, unsigned Offs, MacroExp* E)
+/* Adjust all macro hide set ranges for the macro expansion when the identifier
+** at Idx is replaced with a count of Offs + 1 (if Offs > 0) of identifiers.
+*/
+{
+ if (Offs != 0) {
+ unsigned I;
+
+ for (I = 0; I < CollCount (&E->HideSets); ++I) {
+ HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I);
+ HideRange* This;
+
+ for (This = MHS->HS; This != 0; This = This->Next) {
+ if (Idx < This->Start) {
+ This->Start += Offs;
+ This->End += Offs;
+ } else if (Idx < This->End) {
+ This->End += Offs;
+ }
+ }
+ }
+ }
+}
+
+
+
+static void ME_RemoveToken (unsigned Idx, unsigned Count, MacroExp* E)
+/* Remove the Idx'th identifier token from tracking and offset all hidden
+** ranges accordingly.
+*/
+{
+ unsigned I;
+
+ for (I = 0; I < CollCount (&E->HideSets); ++I) {
+ HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I);
+ HideRange* This;
+ HideRange** Prev;
+
+ for (Prev = &MHS->HS, This = *Prev; This != 0; This = *Prev) {
+ if (Idx < This->Start) {
+ if (This->Start - Idx >= Count) {
+ This->Start -= Count;
+ This->End -= Count;
+ } else {
+ if (This->End - Idx > Count) {
+ This->Start = Idx;
+ This->End -= Count;
+ } else {
+ /* Remove */
+ (*Prev) = This->Next;
+ FreeHideRange (This);
+ continue;
+ }
+ }
+ } else if (Idx < This->End) {
+ if (This->End - Idx > Count) {
+ This->End -= Count;
+ } else {
+ This->End = Idx;
+ }
+
+ if (This->End == This->Start) {
+ /* Remove */
+ (*Prev) = This->Next;
+ FreeHideRange (This);
+ continue;
+ }
+ }
+
+ Prev = &This->Next;
+ }
+ }
+}
+
+
+
+static void ME_HandleSemiNestedMacro (unsigned NameIdx, unsigned LastIdx, MacroExp* E)
+/* Unhide the macro name from all hidesets if it was expanded with an unhidden
+** right parenthesis. This is unspecified but allowed behavior according to
+** ISO/IEC 9899:2018, 6.10.3.4ff.
+*/
+{
+ unsigned I;
+
+ for (I = 0; I < CollCount (&E->HideSets); ++I) {
+ HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I);
+ HideRange* This;
+ HideRange** Prev;
+
+ for (Prev = &MHS->HS, This = *Prev; This != 0; This = *Prev) {
+ if (NameIdx < This->End) {
+ if (NameIdx >= This->Start && LastIdx >= This->End) {
+ This->End = NameIdx;
+ if (This->End == This->Start) {
+ /* Remove */
+ (*Prev) = This->Next;
+ FreeHideRange (This);
+ continue;
+ }
+ }
+ Prev = &This->Next;
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+
+
+static void ME_AddArgHideSets (unsigned Idx, const MacroExp* A, MacroExp* Parent)
+/* Propagate the macro hide sets of the substituted argument starting as the
+** Idx'th identifier of the result.
+*/
+{
+ unsigned I;
+
+ /* Move the hide set generated with in the argument as it will be freed later */
+ for (I = 0; I < CollCount (&A->HideSets); ++I) {
+ HiddenMacro* MHS = CollAtUnchecked (&A->HideSets, I);
+ HideRange* HS;
+
+ for (HS = MHS->HS; HS != 0; HS = HS->Next) {
+ ME_HideMacro (Idx + HS->Start, HS->End - HS->Start, Parent, MHS->M);
+ }
+ }
+}
+
+
+
+static void ME_DoneHideSets (MacroExp* E)
+/* Free all of hidden macros for the macro expansion */
+{
+ unsigned I;
+
+ for (I = 0; I < CollCount (&E->HideSets); ++I) {
+ HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I);
+ FreeHiddenMacro (MHS);
+ }
+ DoneCollection (&E->HideSets);
+}
+
+
+
+static void ME_SetTokLens (MacroExp* E, unsigned TokLen)
+/* Set token lengths and flags for macro expansion struct */
+{
+ E->LastTokLen = TokLen;
+ if ((E->Flags & MES_FIRST_TOKEN) != 0) {
+ E->Flags &= ~MES_FIRST_TOKEN;
+ E->FirstTokLen = E->LastTokLen;
+ } else {
+ E->Flags |= MES_MULTIPLE_TOKEN;
+ }
+}
+
+
+
+static MacroExp* ME_MakeReplaced (MacroExp* A)
+/* Make a replaced version of the argument */
+{
+ /* Replace the parameter with actual argument tokens */
+ if ((A->Flags & MES_HAS_REPLACEMENT) == 0) {
+ A->Replaced = xmalloc (sizeof (MacroExp));
+
+ InitMacroExp (A->Replaced);
+ SB_Reset (&A->Tokens);
+
+ /* Propagate the hide sets */
+ ME_AddArgHideSets (0, A, A->Replaced);
+
+ /* Do macro expansion on the argument */
+ A->Replaced->IdentCount = ReplaceMacros (&A->Tokens,
+ &A->Replaced->Tokens,
+ A->Replaced,
+ MSM_IN_ARG_EXPANSION);
+
+ A->Flags |= MES_HAS_REPLACEMENT;
+ }
+
+ return A->Replaced != 0 ? A->Replaced : A;
+}
+
+
+
+static MacroExp* ME_GetOriginalArg (const MacroExp* E, unsigned Index)
+/* Return an actual macro argument with the given index */
+{
+ return CollAt (&E->Args, Index);
+}
+
+
+
+static MacroExp* ME_GetReplacedArg (const MacroExp* E, unsigned Index)
+/* Return a replaced macro argument with the given index */
+{
+ return ME_MakeReplaced (CollAt (&E->Args, Index));
+}
+
+
+
+static MacroExp* ME_AppendArg (MacroExp* E, MacroExp* Arg)
+/* Add a copy of Arg to the list of actual macro arguments.
+** NOTE: This function will clear the token sequence of Arg!
+*/
+{
+ MacroExp* A = xmalloc (sizeof (MacroExp));
+
+ /* Initialize our MacroExp structure */
+ InitMacroExp (A);
+
+ /* Copy info about the original strings */
+ A->IdentCount = Arg->IdentCount;
+ A->Flags = Arg->Flags;
+ A->FirstTokLen = Arg->FirstTokLen;
+ A->LastTokLen = Arg->LastTokLen;
+
+ /* Move the contents of Arg to A */
+ SB_Move (&A->Tokens, &Arg->Tokens);
+
+ /* Add A to the list of actual arguments */
+ CollAppend (&E->Args, A);
+
+ return A;
+}
+
+
+
+static void ME_ClearArgs (MacroExp* E)
+/* Clear all read arguments for macro expansion */
+{
+ unsigned I;
+
+ /* Delete the list with actual arguments */
+ for (I = 0; I < CollCount (&E->Args); ++I) {
+ MacroExp* A = CollAtUnchecked (&E->Args, I);
+
+ /* Destroy the macro expansion structure and then free memory allocated
+ ** for it.
+ */
+ DoneMacroExp (A);
+ xfree (A);
+ }
+
+ DoneCollection (&E->Args);
+ InitCollection (&E->Args);
+}
+
+
+
+static int ME_IsNextArgVariadic (const MacroExp* E, const Macro* M)
+/* Return true if the next actual argument we will add is a variadic one */
+{
+ return (M->Variadic &&
+ M->ParamCount == (int) CollCount (&E->Args) + 1);
+}
+
+
+
static MacroExp* InitMacroExp (MacroExp* E)
/* Initialize a MacroExp structure */
{
- InitCollection (&E->ActualArgs);
- SB_Init (&E->Replacement);
+ InitCollection (&E->Args);
+ InitCollection (&E->HideSets);
+ SB_Init (&E->Tokens);
+ E->IdentCount = 0;
+ E->Flags = MES_FIRST_TOKEN;
+ E->Replaced = 0;
+ E->FirstTokLen = 0;
+ E->LastTokLen = 0;
return E;
}
@@ -220,48 +713,70 @@ static MacroExp* InitMacroExp (MacroExp* E)
static void DoneMacroExp (MacroExp* E)
/* Cleanup after use of a MacroExp structure */
{
- unsigned I;
-
- /* Delete the list with actual arguments */
- for (I = 0; I < CollCount (&E->ActualArgs); ++I) {
- FreeStrBuf (CollAtUnchecked (&E->ActualArgs, I));
+ ME_ClearArgs (E);
+ ME_DoneHideSets (E);
+ SB_Done (&E->Tokens);
+ if (E->Replaced != 0) {
+ DoneMacroExp (E->Replaced);
}
- DoneCollection (&E->ActualArgs);
- SB_Done (&E->Replacement);
}
-static void ME_AppendActual (MacroExp* E, StrBuf* Arg)
-/* Add a copy of Arg to the list of actual macro arguments.
-** NOTE: This function will clear Arg!
-*/
+/*****************************************************************************/
+/* Rescan input stack */
+/*****************************************************************************/
+
+
+
+static void PushRescanLine (RescanInputStack* RIS, StrBuf* L, unsigned LastTokLen)
+/* Push an input line to the rescan input stack */
{
- /* Create a new string buffer */
- StrBuf* A = NewStrBuf ();
-
- /* Move the contents of Arg to A */
- SB_Move (A, Arg);
-
- /* Add A to the actual arguments */
- CollAppend (&E->ActualArgs, A);
+ CollAppend (&RIS->Lines, L);
+ /* Abuse the pointer to store an unsigned */
+ CollAppend (&RIS->LastTokLens, (void*)(uintptr_t)LastTokLen);
}
-static StrBuf* ME_GetActual (MacroExp* E, unsigned Index)
-/* Return an actual macro argument with the given index */
+static void PopRescanLine (void)
+/* Pop and free a rescan input line if it reaches the end */
{
- return CollAt (&E->ActualArgs, Index);
+ if (CurC == '\0' && CollCount (&CurRescanStack->Lines) > 1) {
+ FreeStrBuf (CollPop (&CurRescanStack->Lines));
+ InitLine (CollLast (&CurRescanStack->Lines));
+ CollPop (&CurRescanStack->LastTokLens);
+ }
}
-static int ME_ArgIsVariadic (const MacroExp* E, const Macro* M)
-/* Return true if the next actual argument we will add is a variadic one */
+static void InitRescanInputStack (RescanInputStack* RIS)
+/* Init a RescanInputStack struct */
{
- return (M->Variadic &&
- M->ArgCount == (int) CollCount (&E->ActualArgs) + 1);
+ InitCollection (&RIS->Lines);
+ InitCollection (&RIS->LastTokLens);
+ RIS->PrevTok = 0;
+}
+
+
+
+static void DoneRescanInputStack (RescanInputStack* RIS)
+/* Free a RescanInputStack struct. RIS must be non-NULL. */
+{
+ /* Free pushed input lines */
+ while (CollCount (&RIS->Lines) > 1) {
+ FreeStrBuf (CollPop (&RIS->Lines));
+ }
+ /* Switch back to the old input stack */
+ InitLine (CollPop (&RIS->Lines));
+
+ /* Free any remaining pp-tokens used for concatenation check */
+ FreeStrBuf (RIS->PrevTok);
+
+ /* Done */
+ DoneCollection (&RIS->Lines);
+ DoneCollection (&RIS->LastTokLens);
}
@@ -292,21 +807,42 @@ static int MacName (char* Ident)
+static void CheckForBadIdent (const char* Ident, int Std, const Macro* M)
+/* Check for and warning on problematic identifiers */
+{
+ if (Std >= STD_C99 &&
+ (M == 0 || !M->Variadic) &&
+ strcmp (Ident, "__VA_ARGS__") == 0) {
+ /* __VA_ARGS__ cannot be used as a macro parameter name in post-C89
+ ** mode.
+ */
+ PPWarning ("__VA_ARGS__ can only appear in the expansion of a C99 variadic macro");
+ }
+}
+
+
+
static void AddPreLine (StrBuf* Str)
/* Add newlines to the string buffer */
{
+ /* No need to prettify the non-exist output */
if (!PreprocessOnly) {
PendingNewLines = 0;
+ ContinuedLines = 0;
return;
}
+ /* We'll adjust the line number later if necessary */
+ PendingNewLines += ContinuedLines;
+
if (FileChanged || PendingNewLines > 6) {
/* Output #line directives as source info */
StrBuf Comment = AUTO_STRBUF_INITIALIZER;
if (SB_NotEmpty (Str) && SB_LookAtLast (Str) != '\n') {
SB_AppendChar (Str, '\n');
}
- SB_Printf (&Comment, "#line %u \"%s\"\n", GetCurrentLine (), GetCurrentFile ());
+ SB_Printf (&Comment, "#line %u \"%s\"\n",
+ GetCurrentLineNum () - ContinuedLines, GetCurrentFilename ());
SB_Append (Str, &Comment);
} else {
/* Output new lines */
@@ -317,6 +853,7 @@ static void AddPreLine (StrBuf* Str)
}
FileChanged = 0;
PendingNewLines = 0;
+ ContinuedLines = 0;
}
@@ -364,12 +901,12 @@ static void Stringize (StrBuf* Source, StrBuf* Target)
static void OldStyleComment (void)
-/* Remove an old style C comment from line. */
+/* Remove an old style C comment from line */
{
/* Remember the current line number, so we can output better error
** messages if the comment is not terminated in the current file.
*/
- unsigned StartingLine = GetCurrentLine ();
+ unsigned StartingLine = GetCurrentLineNum ();
/* Skip the start of comment chars */
NextChar ();
@@ -383,6 +920,7 @@ static void OldStyleComment (void)
StartingLine);
return;
}
+ ++PendingNewLines;
} else {
if (CurC == '/' && NextC == '*') {
PPWarning ("'/*' found inside a comment");
@@ -399,11 +937,13 @@ static void OldStyleComment (void)
static void NewStyleComment (void)
-/* Remove a new style C comment from line. */
+/* Remove a new style C comment from line */
{
/* Diagnose if this is unsupported */
- if (IS_Get (&Standard) < STD_C99) {
+ if (IS_Get (&Standard) < STD_C99 && !AllowNewComments) {
PPError ("C++ style comments are not allowed in C89");
+ PPNote ("(this will be reported only once per input file)");
+ AllowNewComments = 1;
}
/* Beware: Because line continuation chars are handled when reading
@@ -431,6 +971,8 @@ static int SkipWhitespace (int SkipLines)
{
int Skipped = 0;
int NewLine = 0;
+
+ /* Rescanning */
while (1) {
if (IsSpace (CurC)) {
NextChar ();
@@ -441,14 +983,51 @@ static int SkipWhitespace (int SkipLines)
} else if (CurC == '/' && NextC == '/') {
NewStyleComment ();
Skipped = 1;
- } else if (CurC == '\0' && SkipLines) {
- /* End of line, read next */
- if (NextLine () != 0) {
- ++PendingNewLines;
- NewLine = 1;
- Skipped = 0;
+ } else if (CurC == '\0') {
+ /* End of line */
+ if (CurRescanStack != 0 &&
+ CollCount (&CurRescanStack->Lines) > 1 &&
+ Line == CollLast (&CurRescanStack->Lines)) {
+
+ unsigned LastTokLen = (unsigned)(uintptr_t)CollLast (&CurRescanStack->LastTokLens);
+
+ /* Check for potentially merged tokens */
+ if (Skipped == 0 && LastTokLen != 0) {
+ /* Get the following input */
+ StrBuf* Next = CollAtUnchecked (&CurRescanStack->Lines,
+ CollCount (&CurRescanStack->Lines) - 2);
+ char C = SB_Peek (Next);
+
+ /* We cannot check right now if the next pp-token may be a
+ ** macro.
+ */
+ if (IsIdent (C)) {
+ /* Memorize the previous pp-token and check it later */
+ LazyCheckNextPPTok (Line, LastTokLen);
+ } else if (C != '\0' && !IsSpace (C)) {
+ /* If the two adjacent pp-tokens could be put together
+ ** to form a new one, we have to separate them with an
+ ** additional space.
+ */
+ Skipped = CheckPastePPTok (Line, LastTokLen, SB_Peek (Next));
+ }
+
+ }
+
+ /* switch back to previous input */
+ PopRescanLine ();
+
+ } else if (SkipLines) {
+ /* Read next line */
+ if (NextLine () != 0) {
+ ++PendingNewLines;
+ NewLine = 1;
+ Skipped = 0;
+ } else {
+ /* End of input */
+ break;
+ }
} else {
- /* End of input */
break;
}
} else {
@@ -456,6 +1035,7 @@ static int SkipWhitespace (int SkipLines)
break;
}
}
+
return Skipped != 0 ? Skipped : -(NewLine != 0);
}
@@ -490,11 +1070,28 @@ static void CopyHeaderNameToken (StrBuf* Target)
+static int IsQuotedString (void)
+/* Retrun 1 if the incoming characters indicate a string literal or character
+** constant, otherwise return 0.
+*/
+{
+ return IsQuote (CurC) || IsWideQuoted (CurC, NextC);
+}
+
+
+
static void CopyQuotedString (StrBuf* Target)
/* Copy a single or double quoted string from the input to Target. */
{
/* Remember the quote character, copy it to the target buffer and skip it */
- char Quote = CurC;
+ char Quote;
+
+ if (CurC == 'L') {
+ SB_AppendChar (Target, CurC);
+ NextChar ();
+ }
+
+ Quote = CurC;
SB_AppendChar (Target, CurC);
NextChar ();
@@ -521,6 +1118,384 @@ static void CopyQuotedString (StrBuf* Target)
+static int GetPunc (char* S)
+/* Parse a punctuator token. Return 1 and store the parsed token string into S
+** on success, otherwise just return 0.
+*/
+{
+ char C;
+ switch (CurC) {
+
+ case '[':
+ case ']':
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ case '~':
+ case '?':
+ case ':':
+ case ';':
+ case ',':
+ /* C */
+ *S++ = CurC;
+ NextChar ();
+ break;
+
+ case '=':
+ case '#':
+ /* C or CC */
+ C = *S++ = CurC;
+ NextChar ();
+ if (CurC == C) {
+ *S++ = C;
+ NextChar ();
+ }
+ break;
+
+ case '*':
+ case '/':
+ case '%':
+ case '^':
+ case '!':
+ /* C or C= */
+ *S++ = CurC;
+ NextChar ();
+ if (CurC == '=') {
+ *S++ = CurC;
+ NextChar ();
+ }
+ break;
+
+ case '+':
+ case '&':
+ case '|':
+ /* C, CC or C= */
+ C = *S++ = CurC;
+ NextChar ();
+ if (CurC == C || CurC == '=') {
+ *S++ = CurC;
+ NextChar ();
+ }
+ break;
+
+ case '<':
+ case '>':
+ /* C, CC, C= or CC= */
+ C = *S++ = CurC;
+ NextChar ();
+ if (CurC == C) {
+ *S++ = CurC;
+ if (NextC == '=') {
+ *S++ = NextC;
+ NextChar ();
+ }
+ NextChar ();
+ } else if (CurC == '=') {
+ *S++ = CurC;
+ NextChar ();
+ }
+ break;
+
+ case '-':
+ /* C, CC, C= or C> */
+ *S++ = CurC;
+ NextChar ();
+ switch (CurC) {
+ case '-':
+ case '=':
+ case '>':
+ *S++ = CurC;
+ NextChar ();
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '.':
+ /* C or CCC */
+ *S++ = CurC;
+ NextChar ();
+ if (CurC == '.' && NextC == '.') {
+ *S++ = CurC;
+ *S++ = NextC;
+ NextChar ();
+ NextChar ();
+ }
+ break;
+
+ default:
+ return 0;
+
+ }
+
+ *S = '\0';
+ return 1;
+}
+
+
+
+static int CheckPastePPTok (StrBuf* Source, unsigned TokLen, char Next)
+/* Return 1 if the last pp-tokens from Source could be concatenated with any
+** characters from Appended to form a new valid one.
+*/
+{
+ char C;
+ unsigned NewTokLen;
+ StrBuf* OldSource;
+ StrBuf Src = AUTO_STRBUF_INITIALIZER;
+ StrBuf Buf = AUTO_STRBUF_INITIALIZER;
+
+ if (TokLen == 0 || IsBlank (SB_LookAtLast (Source))) {
+ return 0;
+ }
+
+ PRECONDITION (SB_GetLen (Source) >= TokLen);
+
+ /* Special casing "..", "/ /" and "/ *" that are not pp-tokens but still
+ ** need be separated.
+ */
+ C = SB_LookAt (Source, SB_GetLen (Source) - TokLen);
+ if ((C == '.' && Next == '.') || (C == '/' && (Next == '/' || Next == '*'))) {
+ return 1;
+ }
+
+ SB_CopyBuf (&Src, SB_GetConstBuf (Source) + SB_GetLen (Source) - TokLen, TokLen);
+ SB_AppendChar (&Src, Next);
+
+ SB_Reset (&Src);
+ OldSource = InitLine (&Src);
+
+ if (IsPPNumber (CurC, NextC)) {
+ /* PP-number */
+ CopyPPNumber (&Buf);
+ } else if (IsQuotedString ()) {
+ /* Quoted string */
+ CopyQuotedString (&Buf);
+ } else {
+ ident Ident;
+ if (GetPunc (Ident)) {
+ /* Punctuator */
+ SB_CopyStr (&Buf, Ident);
+ } else if (IsSym (Ident)) {
+ /* Identifier */
+ SB_CopyStr (&Buf, Ident);
+ }
+ }
+
+ NewTokLen = SB_GetLen (&Buf);
+
+ SB_Done (&Buf);
+ SB_Done (&Src);
+
+ /* Restore old source */
+ InitLine (OldSource);
+
+ /* Return if concatenation succeeded */
+ return NewTokLen != TokLen;
+}
+
+
+
+static int TryPastePPTok (StrBuf* Target,
+ StrBuf* Appended,
+ unsigned FirstTokLen,
+ unsigned SecondTokLen)
+/* Paste the whole appened pp-token sequence onto the end of the target
+** pp-token sequence. Diagnose if it fails to form a valid pp-token with the
+** two pp-tokens pasted together. Return 1 if succeeds.
+*/
+{
+ unsigned TokLen;
+ StrBuf* OldSource;
+ StrBuf Src = AUTO_STRBUF_INITIALIZER;
+ StrBuf Buf = AUTO_STRBUF_INITIALIZER;
+
+ if (FirstTokLen == 0 || SecondTokLen == 0) {
+ SB_Append (Target, Appended);
+ return 1;
+ }
+
+ /* Since we need to concatenate the token sequences, remove the
+ ** last whitespace that was added to target, since it must come
+ ** from the input.
+ */
+ if (IsBlank (SB_LookAtLast (Target))) {
+ SB_Drop (Target, 1);
+ }
+
+ PRECONDITION (SB_GetLen (Target) >= FirstTokLen &&
+ SB_GetLen (Appended) >= SecondTokLen);
+
+ /* Special casing "..", "/ /" and "/ *" */
+ if (FirstTokLen == 1) {
+ char C = SB_LookAt (Target, SB_GetLen (Target) - FirstTokLen);
+ char N = SB_LookAt (Appended, 0);
+ /* Avoid forming a comment introducer or an ellipsis. Note that an
+ ** ellipsis pp-token cannot be formed with macros anyway.
+ */
+ if ((C == '.' && N == '.') || (C == '/' && (N == '/' || N == '*'))) {
+ SB_AppendChar (Target, ' ');
+ SB_Append (Target, Appended);
+ PPWarning ("Pasting formed \"%c%c\", an invalid preprocessing token", C, N);
+
+ return 0;
+ }
+ }
+
+ SB_CopyBuf (&Src, SB_GetConstBuf (Target) + SB_GetLen (Target) - FirstTokLen, FirstTokLen);
+ if (SecondTokLen == SB_GetLen (Appended) || IsSpace (SB_LookAt (Appended, SecondTokLen))) {
+ SB_AppendBuf (&Src, SB_GetConstBuf (Appended), SecondTokLen);
+ } else {
+ SB_AppendBuf (&Src, SB_GetConstBuf (Appended), SecondTokLen + 1);
+ }
+
+ SB_Reset (&Src);
+ OldSource = InitLine (&Src);
+
+ if (IsPPNumber (CurC, NextC)) {
+ /* PP-number */
+ CopyPPNumber (&Buf);
+ } else if (IsQuotedString ()) {
+ /* Quoted string */
+ CopyQuotedString (&Buf);
+ } else {
+ ident Ident;
+ if (GetPunc (Ident)) {
+ /* Punctuator */
+ SB_CopyStr (&Buf, Ident);
+ } else if (IsSym (Ident)) {
+ /* Identifier */
+ SB_CopyStr (&Buf, Ident);
+ } else {
+ /* Unknown */
+ }
+ }
+
+ TokLen = SB_GetLen (&Buf);
+ if (TokLen < FirstTokLen + SecondTokLen) {
+ /* The pasting doesn't form a valid pp-token */
+ while (SB_GetLen (&Buf) < FirstTokLen + SecondTokLen) {
+ SB_AppendChar (&Buf, CurC);
+ NextChar ();
+ }
+ SB_Terminate (&Buf);
+ PPWarning ("Pasting formed \"%s\", an invalid preprocessing token",
+ SB_GetConstBuf (&Buf));
+
+ /* Add a space between the tokens to avoid problems in rescanning */
+ if (TokLen > FirstTokLen) {
+ SB_AppendChar (Target, ' ');
+ }
+ /* Append all remaining tokens */
+ SB_Append (Target, Appended);
+
+ /* No concatenation */
+ TokLen = FirstTokLen;
+ } else {
+ /* Add a space after the merged token if necessary */
+ SB_AppendBuf (Target, SB_GetConstBuf (Appended), SecondTokLen);
+ if (TokLen > FirstTokLen + SecondTokLen) {
+ SB_AppendChar (Target, ' ');
+ }
+ /* Append all remaining tokens */
+ SB_AppendBuf (Target,
+ SB_GetConstBuf (Appended) + SecondTokLen,
+ SB_GetLen (Appended) - SecondTokLen);
+ }
+
+ SB_Done (&Buf);
+ SB_Done (&Src);
+
+ /* Restore old source */
+ InitLine (OldSource);
+
+ /* Return if concatenation succeeded */
+ return TokLen != FirstTokLen;
+}
+
+
+
+static void SeparatePPTok (StrBuf* Target, char Next)
+/* Add a space to target if the previous pp-token could be concatenated with
+** the following character.
+*/
+{
+ if (CurRescanStack->PrevTok != 0) {
+ unsigned Len = SB_GetLen (CurRescanStack->PrevTok) - SB_GetIndex (CurRescanStack->PrevTok);
+
+ /* Check for pp-token pasting */
+ if (CheckPastePPTok (CurRescanStack->PrevTok, Len, Next)) {
+ SB_AppendChar (Target, ' ');
+ }
+ FreeStrBuf (CurRescanStack->PrevTok);
+ CurRescanStack->PrevTok = 0;
+ }
+}
+
+
+
+static void LazyCheckNextPPTok (const StrBuf* Prev, unsigned LastTokLen)
+/* Memorize the previous pp-token(s) to later check for potential pp-token
+** concatenation.
+*/
+{
+ char C;
+ int CheckEllipsis = 0;
+ unsigned NewIndex = SB_GetLen (Prev) - LastTokLen;
+
+ PRECONDITION (SB_GetLen (Prev) >= LastTokLen);
+
+ /* Check for some special cases */
+ C = SB_AtUnchecked (Prev, NewIndex);
+
+ /* We may exclude certain punctuators for speedups. As newer C standards
+ ** could add more punctuators such as "[[", "]]", "::" and so on, this
+ ** check might need changes accordingly.
+ */
+ if (C == '[' || C == ']' || C == '(' || C == ')' ||
+ C == '{' || C == '}' || C == '~' || C == '?' ||
+ C == ':' || C == ';' || C == ',') {
+ /* These punctuators cannot be concatenated */
+ return;
+ }
+
+ /* Special check for .. */
+ if (NewIndex > 0 &&
+ C == '.' &&
+ SB_AtUnchecked (Prev, NewIndex - 1) == '.') {
+ /* Save the preceding '.' as well */
+ CheckEllipsis = 1;
+ }
+
+ if (CurRescanStack->PrevTok != 0) {
+ unsigned OldIndex = SB_GetIndex (CurRescanStack->PrevTok);
+ unsigned OldLen = SB_GetLen (CurRescanStack->PrevTok) - OldIndex;
+ unsigned NewLen = SB_GetLen (Prev) - NewIndex;
+ if (OldLen == NewLen &&
+ strncmp (SB_GetConstBuf (CurRescanStack->PrevTok) + OldIndex - CheckEllipsis,
+ SB_GetConstBuf (Prev) + NewIndex - CheckEllipsis,
+ OldLen + CheckEllipsis) == 0) {
+ /* Same pp-token, keep using the old one */
+ } else {
+ /* Logic error */
+ SB_Terminate (CurRescanStack->PrevTok);
+ Internal ("Unchecked pp-token concatenation: \"%s\"",
+ SB_GetConstBuf (CurRescanStack->PrevTok) + SB_GetIndex (CurRescanStack->PrevTok));
+ }
+ } else {
+ /* Memorize the current line */
+ CurRescanStack->PrevTok = NewStrBuf ();
+ SB_CopyBuf (CurRescanStack->PrevTok,
+ SB_GetConstBuf (Prev) + NewIndex - CheckEllipsis,
+ LastTokLen + CheckEllipsis);
+ SB_Reset (CurRescanStack->PrevTok);
+ }
+}
+
+
+
static int CheckExtraTokens (const char* Name)
/* Check for extra tokens at the end of the directive. Return 1 if there are
** extra tokens, otherwise 0.
@@ -542,317 +1517,629 @@ static int CheckExtraTokens (const char* Name)
-static void ReadMacroArgs (MacroExp* E, const Macro* M, int MultiLine)
-/* Identify the arguments to a macro call as-is */
+static unsigned ReadMacroArgs (unsigned NameIdx, MacroExp* E, const Macro* M, int MultiLine)
+/* Identify the arguments to a macro call as-is. Return the total count of
+** identifiers and right parentheses in the read argument list.
+*/
{
- int MissingParen = 0;
- unsigned Parens; /* Number of open parenthesis */
- StrBuf Arg = AUTO_STRBUF_INITIALIZER;
+ unsigned Idx = 0;
+ unsigned CountInArg = 0;
+ unsigned Parens = 0; /* Number of open parenthesis */
+ ident Ident;
+ MacroExp Arg;
+
+ InitMacroExp (&Arg);
/* Eat the left paren */
NextChar ();
/* Read the actual macro arguments */
- Parens = 0;
while (1) {
/* Squeeze runs of blanks within an arg */
- int OldPendingNewLines = PendingNewLines;
+ unsigned OldPendingNewLines = PendingNewLines;
int Skipped = SkipWhitespace (MultiLine);
- if (MultiLine && CurC == '#') {
- int Newlines = 0;
- while (CurC == '#') {
+ /* Directives can only be found in an argument list that spans
+ ** multiple lines.
+ */
+ if (MultiLine && OldPendingNewLines < PendingNewLines && CurC == '#') {
+ unsigned Newlines = 0;
+
+ while (OldPendingNewLines < PendingNewLines && CurC == '#') {
Newlines += PendingNewLines - OldPendingNewLines;
PendingNewLines = OldPendingNewLines;
OldPendingNewLines = 0;
Skipped = ParseDirectives (MSM_IN_ARG_LIST) || Skipped;
- Skipped = SkipWhitespace (MultiLine) || Skipped;
}
PendingNewLines += Newlines;
}
- if (Skipped && SB_NotEmpty (&Arg)) {
- SB_AppendChar (&Arg, ' ');
+
+ /* Append a space as a separator */
+ if (Skipped && SB_NotEmpty (&Arg.Tokens)) {
+ SB_AppendChar (&Arg.Tokens, ' ');
+ Skipped = 1;
+ } else {
+ Skipped = 0;
}
- if (CurC == '(') {
- /* Nested parenthesis */
- SB_AppendChar (&Arg, CurC);
- NextChar ();
- ++Parens;
+ /* Finish reading the current argument if we are not inside nested
+ ** parentheses or a variadic macro argument.
+ */
+ if (Parens == 0 &&
+ ((CurC == ',' && !ME_IsNextArgVariadic (E, M)) || CurC == ')')) {
- } else if (IsQuote (CurC)) {
-
- /* Quoted string - just copy */
- CopyQuotedString (&Arg);
-
- } else if (CurC == ',' || CurC == ')') {
-
- if (Parens) {
- /* Comma or right paren inside nested parenthesis */
- if (CurC == ')') {
- --Parens;
- }
- SB_AppendChar (&Arg, CurC);
- NextChar ();
- } else if (CurC == ',' && ME_ArgIsVariadic (E, M)) {
- /* It's a comma, but we're inside a variadic macro argument, so
- ** just copy it and proceed.
- */
- SB_AppendChar (&Arg, CurC);
- NextChar ();
- } else {
- /* End of actual argument. Remove whitespace from the end. */
- while (IsSpace (SB_LookAtLast (&Arg))) {
- SB_Drop (&Arg, 1);
- }
-
- /* If this is not the single empty argument for a macro with
- ** an empty argument list, remember it.
- */
- if (CurC != ')' || SB_NotEmpty (&Arg) || M->ArgCount > 0) {
- ME_AppendActual (E, &Arg);
- }
-
- /* Check for end of macro param list */
- if (CurC == ')') {
- NextChar ();
- break;
- }
-
- /* Start the next param */
- NextChar ();
- SB_Clear (&Arg);
+ /* End of actual argument. Remove whitespace from the end. */
+ while (IsBlank (SB_LookAtLast (&Arg.Tokens))) {
+ SB_Drop (&Arg.Tokens, 1);
}
+
+ /* If this is not the single empty argument for a macro with an
+ ** empty argument list, remember it.
+ */
+ if (CurC != ')' ||
+ CollCount (&E->Args) > 0 ||
+ SB_NotEmpty (&Arg.Tokens) ||
+ M->ParamCount > 0) {
+ MacroExp* A = ME_AppendArg (E, &Arg);
+ unsigned I;
+
+ /* Copy the hide sets from the argument list */
+ for (I = 0; I < CollCount (&E->HideSets); ++I) {
+ HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I);
+ HideRange* HS;
+
+ for (HS = MHS->HS; HS != 0; HS = HS->Next) {
+ /* Get the correct hide range */
+ unsigned Start = NameIdx + 1 + Idx;
+ unsigned Len;
+ if (HS->Start < Start) {
+ if (HS->End > Start) {
+ Len = HS->End - Start;
+ } else {
+ /* Out of the range */
+ continue;
+ }
+ Start = 0;
+ } else {
+ Len = HS->End - HS->Start;
+ Start = HS->Start - Start;
+ }
+ if (Start + Len > Idx + CountInArg) {
+ if (Idx + CountInArg > Start) {
+ Len = Idx + CountInArg - Start;
+ } else {
+ /* Out of the range */
+ break;
+ }
+ }
+ ME_HideMacro (Start, Len, A, MHS->M);
+ }
+ }
+
+ /* More argument info */
+ A->IdentCount = CountInArg;
+ }
+
+ Idx += CountInArg;
+
+ /* Check for end of macro param list */
+ if (CurC == ')') {
+ /* Count right parens */
+ ++Idx;
+ NextChar ();
+ break;
+ }
+
+ /* Start the next param */
+ NextChar ();
+ DoneMacroExp (&Arg);
+ InitMacroExp (&Arg);
+ CountInArg = 0;
+
+ continue;
+
} else if (CurC == '\0') {
+
/* End of input inside macro argument list */
PPError ("Unterminated argument list invoking macro '%s'", M->Name);
- MissingParen = 1;
+ Parens = -1;
ClearLine ();
+ E->Flags |= MES_ERROR;
+ Idx = 0;
break;
+
} else {
- /* Just copy the character */
- SB_AppendChar (&Arg, CurC);
- NextChar ();
+ unsigned LastLen = SB_GetLen (&Arg.Tokens);
+
+ if (IsSym (Ident)) {
+ /* Just copy the identifier */
+ SB_AppendStr (&Arg.Tokens, Ident);
+
+ /* Count identifiers */
+ ++CountInArg;
+
+ /* Used for concatentation check */
+ if ((Arg.Flags & MES_FIRST_TOKEN) != 0) {
+ Arg.Flags |= MES_BEGIN_WITH_IDENT;
+ }
+ } else if (IsPPNumber (CurC, NextC)) {
+ /* Copy a pp-number */
+ CopyPPNumber (&Arg.Tokens);
+ } else if (IsQuotedString ()) {
+ /* Quoted string - just copy */
+ CopyQuotedString (&Arg.Tokens);
+ } else if (GetPunc (Ident)) {
+ /* Check nested parentheses */
+ if (Ident[0] == '(') {
+ /* Opening nested parenthesis */
+ ++Parens;
+ } else if (Ident[0] == ')') {
+ /* Closing nested parenthesis */
+ --Parens;
+
+ /* Count right parens */
+ ++CountInArg;
+ }
+ /* Just copy the punctuator */
+ SB_AppendStr (&Arg.Tokens, Ident);
+ } else {
+ /* Just copy the character */
+ SB_AppendChar (&Arg.Tokens, CurC);
+ NextChar ();
+
+ /* But don't count it */
+ ++LastLen;
+ }
+
+ /* Used for concatentation check */
+ ME_SetTokLens (&Arg, SB_GetLen (&Arg.Tokens) - LastLen - Skipped);
}
}
/* Compare formal and actual argument count */
- if (CollCount (&E->ActualArgs) != (unsigned) M->ArgCount) {
-
- if (!MissingParen) {
- /* Argument count mismatch */
- PPError ("Macro argument count mismatch");
+ if (CollCount (&E->Args) < (unsigned) M->ParamCount) {
+ /* Check further only when the parentheses are paired */
+ if (Parens == 0) {
+ /* Specially casing variable argument */
+ if (M->Variadic &&
+ M->ParamCount > 0 &&
+ CollCount (&E->Args) + 1 == (unsigned) M->ParamCount) {
+ /* The variable argument is left out entirely */
+ E->Flags |= MES_NO_VA_COMMA;
+ if (IS_Get (&Standard) < STD_CC65) {
+ PPWarning ("ISO C does not permit leaving out the comma before the variable argument");
+ }
+ } else {
+ /* Too few argument */
+ PPError ("Macro \"%s\" passed only %u arguments, but requires %u",
+ M->Name, CollCount (&E->Args), (unsigned) M->ParamCount);
+ }
}
/* Be sure to make enough empty arguments available */
- SB_Clear (&Arg);
- while (CollCount (&E->ActualArgs) < (unsigned) M->ArgCount) {
- ME_AppendActual (E, &Arg);
+ DoneMacroExp (&Arg);
+ InitMacroExp (&Arg);
+ Arg.Flags |= MES_HAS_REPLACEMENT;
+ while (CollCount (&E->Args) < (unsigned) M->ParamCount) {
+ ME_AppendArg (E, &Arg);
}
+ } else if (Parens == 0 && CollCount (&E->Args) > (unsigned) M->ParamCount) {
+ /* Too many arguments */
+ PPError ("Macro \"%s\" passed %u arguments, but takes just %u",
+ M->Name, CollCount (&E->Args), (unsigned) M->ParamCount);
}
- /* Deallocate string buf resources */
- SB_Done (&Arg);
+ /* Deallocate argument resources */
+ DoneMacroExp (&Arg);
+
+ /* Return the total count of identifiers and right parentheses in the
+ ** argument list.
+ */
+ return Idx;
}
-static void SubstMacroArgs (MacroExp* E, Macro* M)
-/* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */
+static unsigned SubstMacroArgs (unsigned NameIdx, StrBuf* Target, MacroExp* E, Macro* M, unsigned* IdentCount)
+/* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff.
+** Return the length of the last pp-token in the result and output the count
+** of identifiers and right parentheses in the result to *IdentCount.
+*/
{
+ unsigned Idx = NameIdx;
ident Ident;
- int ArgIdx;
+ unsigned TokLen = 0;
+ int ParamIdx;
StrBuf* OldSource;
- StrBuf* Arg;
- int HaveSpace;
+ int HaveSpace = 0;
+ StrBuf Buf = AUTO_STRBUF_INITIALIZER; /* Temporary buffer */
+ /* Remember the current input stack and disable it for now */
+ Collection* OldInputStack = UseInputStack (0);
+
+ /* Remember the current input and switch to the macro replacement */
+ unsigned OldIndex = SB_GetIndex (&M->Replacement);
- /* Remember the current input and switch to the macro replacement. */
- int OldIndex = SB_GetIndex (&M->Replacement);
SB_Reset (&M->Replacement);
OldSource = InitLine (&M->Replacement);
- /* Argument handling loop */
+ /* If the macro expansion replaces an function-like macro with an argument
+ ** list containing a right parenthesis outside the hidesets of previously
+ ** replaced macros, stop those hidesets from this replacement. This is not
+ ** required by the standard but just to match up with other major C
+ ** compilers.
+ */
+ ME_HandleSemiNestedMacro (NameIdx, NameIdx + *IdentCount, E);
+
+ /* Substitution loop */
while (CurC != '\0') {
+ int NeedPaste = 0;
/* If we have an identifier, check if it's a macro */
if (IsSym (Ident)) {
- /* Check if it's a macro argument */
- if ((ArgIdx = FindMacroArg (M, Ident)) >= 0) {
+ /* Remember and skip any following whitespace */
+ HaveSpace = SkipWhitespace (0);
- /* A macro argument. Get the corresponding actual argument. */
- Arg = ME_GetActual (E, ArgIdx);
-
- /* Copy any following whitespace */
- HaveSpace = SkipWhitespace (0);
+ /* Check if it's a macro parameter */
+ if ((ParamIdx = FindMacroParam (M, Ident)) >= 0) {
/* If a ## operator follows, we have to insert the actual
- ** argument as is, otherwise it must be macro replaced.
+ ** argument as-is, otherwise it must be macro-replaced.
*/
if (CurC == '#' && NextC == '#') {
- /* ### Add placemarker if necessary */
- SB_Append (&E->Replacement, Arg);
+ /* Get the corresponding actual argument */
+ const MacroExp* A = ME_GetOriginalArg (E, ParamIdx);
+
+ /* Separate with a white space if necessary */
+ if (CheckPastePPTok (Target, TokLen, SB_Peek (&A->Tokens))) {
+ SB_AppendChar (Target, ' ');
+ }
+
+ /* For now we need no placemarkers */
+ SB_Append (Target, &A->Tokens);
+
+ /* Adjust tracking */
+ ME_OffsetHideSets (Idx, A->IdentCount, E);
+ Idx += A->IdentCount;
+
+ /* This will be used for concatenation */
+ TokLen = A->LastTokLen;
} else {
- /* Replace the formal argument by a macro replaced copy
- ** of the actual.
- */
- SB_Reset (Arg);
- MacroReplacement (Arg, &E->Replacement, 0);
+ /* Get the corresponding macro-replaced argument */
+ const MacroExp* A = ME_GetReplacedArg (E, ParamIdx);
- /* If we skipped whitespace before, re-add it now */
- if (HaveSpace) {
- SB_AppendChar (&E->Replacement, ' ');
+ /* Separate with a white space if necessary */
+ if (CheckPastePPTok (Target, TokLen, SB_Peek (&A->Tokens))) {
+ SB_AppendChar (Target, ' ');
}
- }
+ /* Append the replaced string */
+ SB_Append (Target, &A->Tokens);
+
+ /* Insert the range of identifiers to parent preceding this argument */
+ ME_OffsetHideSets (Idx, A->IdentCount, E);
+
+ /* Add hide range */
+ ME_AddArgHideSets (Idx, A, E);
+
+ /* Adjust tracking */
+ Idx += A->IdentCount;
+
+ /* May be used for later pp-token merge check */
+ TokLen = A->LastTokLen;
+ }
} else {
/* An identifier, keep it */
- SB_AppendStr (&E->Replacement, Ident);
+ SB_AppendStr (Target, Ident);
+ /* Adjust tracking */
+ ME_OffsetHideSets (Idx, 1, E);
+ ++Idx;
+
+ /* May be used for later concatenation */
+ TokLen = strlen (Ident);
}
+ /* Special casing for 'L' prefixing '#' */
+ if (TokLen == 1 && SB_LookAtLast (Target) == 'L' && CurC == '#') {
+ HaveSpace = 1;
+ }
+
+ /* Squeeze and add the skipped whitespace back for consistency */
+ if (HaveSpace && !IsBlank (SB_LookAtLast (Target))) {
+ SB_AppendChar (Target, ' ');
+ }
+
+ /* Done with this substituted argument */
+ continue;
+
} else if (CurC == '#' && NextC == '#') {
- /* ## operator. */
+ /* ## operator */
NextChar ();
NextChar ();
SkipWhitespace (0);
- /* Since we need to concatenate the token sequences, remove
- ** any whitespace that was added to target, since it must come
- ** from the input.
- */
- while (IsSpace (SB_LookAtLast (&E->Replacement))) {
- SB_Drop (&E->Replacement, 1);
- }
-
/* If the next token is an identifier which is a macro argument,
- ** replace it, otherwise do nothing.
+ ** replace it, otherwise just add it.
*/
if (IsSym (Ident)) {
+ unsigned NewCount = 1;
- /* Check if it's a macro argument */
- if ((ArgIdx = FindMacroArg (M, Ident)) >= 0) {
+ /* Check if it's a macro parameter */
+ if ((ParamIdx = FindMacroParam (M, Ident)) >= 0) {
- /* Get the corresponding actual argument and add it. */
- SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx));
+ /* Get the corresponding actual argument */
+ MacroExp* A = ME_GetOriginalArg (E, ParamIdx);
+
+ /* Insert the range of identifiers to parent preceding this argument */
+ ME_OffsetHideSets (Idx, A->IdentCount, E);
+
+ /* Add hide range */
+ ME_AddArgHideSets (Idx, A, E);
+
+ /* Adjust tracking */
+ NewCount = A->IdentCount;
+
+ /* If the preceding pp-token is not a placemarker and is
+ ** concatenated to with an identifier, the count of tracked
+ ** identifiers is then one less.
+ */
+ if (TryPastePPTok (Target, &A->Tokens, TokLen, A->FirstTokLen)) {
+ if (TokLen > 0 && (A->Flags & MES_BEGIN_WITH_IDENT) != 0) {
+ --NewCount;
+ ME_RemoveToken (Idx, 1, E);
+ }
+ if ((A->Flags & MES_MULTIPLE_TOKEN) == 0) {
+ TokLen += A->FirstTokLen;
+ } else {
+ TokLen = A->LastTokLen;
+ }
+ } else {
+ TokLen = A->LastTokLen;
+ }
} else {
- /* Just an ordinary identifier - add as is */
- SB_AppendStr (&E->Replacement, Ident);
+ unsigned Len;
+
+ /* Just an ordinary identifier - add as-is */
+ SB_CopyStr (&Buf, Ident);
+
+ /* If the preceding pp-token is not a placemarker and is
+ ** concatenated to with an identifier, the count of tracked
+ ** identifiers is then one less.
+ */
+ Len = SB_GetLen (&Buf);
+ if (TryPastePPTok (Target, &Buf, TokLen, Len)) {
+ if (TokLen > 0) {
+ --NewCount;
+ }
+ TokLen += Len;
+ } else {
+ TokLen = Len;
+ }
+
+ /* Adjust tracking */
+ ME_OffsetHideSets (Idx, NewCount, E);
}
+
+ /* Adjust tracking */
+ Idx += NewCount;
+
+ /* Keep the whitespace for consistency */
+ HaveSpace = SkipWhitespace (0);
+ if (HaveSpace && !IsBlank (SB_LookAtLast (Target))) {
+ SB_AppendChar (Target, ' ');
+ }
+
+ /* Done with this concatenated identifier */
+ continue;
}
- } else if (CurC == '#' && M->ArgCount >= 0) {
-
- /* A # operator within a macro expansion of a function like
- ** macro. Read the following identifier and check if it's a
- ** macro parameter.
- */
- NextChar ();
- SkipWhitespace (0);
- if (!IsSym (Ident) || (ArgIdx = FindMacroArg (M, Ident)) < 0) {
- PPError ("'#' is not followed by a macro parameter");
- } else {
- /* Make a valid string from Replacement */
- Arg = ME_GetActual (E, ArgIdx);
- SB_Reset (Arg);
- Stringize (Arg, &E->Replacement);
+ if (CurC != '\0') {
+ /* Non-identifiers may still be pasted together */
+ NeedPaste = 1;
}
- } else if (IsQuote (CurC)) {
- CopyQuotedString (&E->Replacement);
- } else {
- SB_AppendChar (&E->Replacement, CurC);
- NextChar ();
}
+
+ /* Use the temporary buffer */
+ SB_Clear (&Buf);
+ if (IsPPNumber (CurC, NextC)) {
+ CopyPPNumber (&Buf);
+ } else if (IsQuotedString ()) {
+ CopyQuotedString (&Buf);
+ } else {
+ if (CurC == '#' && M->ParamCount >= 0) {
+ /* A # operator within a macro expansion of a function-like
+ ** macro. Read the following identifier and check if it's a
+ ** macro parameter.
+ */
+ NextChar ();
+ SkipWhitespace (0);
+ if (!IsSym (Ident) || (ParamIdx = FindMacroParam (M, Ident)) < 0) {
+ /* Should not happen, but still */
+ Internal ("'#' is not followed by a macro parameter");
+ } else {
+ /* Make a valid string from Replacement */
+ MacroExp* A = ME_GetOriginalArg (E, ParamIdx);
+ SB_Reset (&A->Tokens);
+ Stringize (&A->Tokens, &Buf);
+ }
+ } else if (GetPunc (Ident)) {
+ /* Count right parens. This is OK since they cannot be pasted
+ ** to form different punctuators with others.
+ */
+ if (Ident[0] == ')') {
+ /* Adjust tracking */
+ ME_OffsetHideSets (Idx, 1, E);
+ ++Idx;
+ }
+ SB_AppendStr (&Buf, Ident);
+ } else if (CurC != '\0') {
+ SB_AppendChar (&Buf, CurC);
+ NextChar ();
+ }
+ }
+
+ /* Squeeze any whitespace for consistency. Especially, comments must
+ ** be consumed before fed to the punctuator parser, or their leading
+ ** '/' characters would be parsed wrongly as division operators.
+ */
+ HaveSpace = SkipWhitespace (0);
+
+ if (NeedPaste) {
+ unsigned Len = SB_GetLen (&Buf);
+
+ /* Concatenate pp-tokens */
+ if (TryPastePPTok (Target, &Buf, TokLen, Len)) {
+ TokLen += Len;
+ } else {
+ TokLen = Len;
+ }
+ } else {
+ /* Just append the token */
+ SB_Append (Target, &Buf);
+ TokLen = SB_GetLen (&Buf);
+ }
+
+ if (HaveSpace && !IsBlank (SB_LookAtLast (Target))) {
+ SB_AppendChar (Target, ' ');
+ }
+
}
-#if 0
- /* Remove whitespace from the end of the line */
- while (IsSpace (SB_LookAtLast (&E->Replacement))) {
- SB_Drop (&E->Replacement, 1);
- }
-#endif
+ /* Done with the temporary buffer */
+ SB_Done (&Buf);
+
+ /* Remove the macro name itself together with the arguments (if any) */
+ ME_RemoveToken (Idx, 1 + *IdentCount, E);
+
+ /* Hide this macro for the whole result of this expansion */
+ ME_HideMacro (NameIdx, Idx - NameIdx, E, M);
/* Switch back the input */
+ UseInputStack (OldInputStack);
InitLine (OldSource);
SB_SetIndex (&M->Replacement, OldIndex);
+
+ /* Set the count of identifiers and right parentheses in the result */
+ *IdentCount = Idx - NameIdx;
+
+ /* Return the length of the last pp-token */
+ return TokLen;
}
-static void ExpandMacro (StrBuf* Target, Macro* M, int MultiLine)
-/* Expand a macro into Target */
+static unsigned ExpandMacro (unsigned Idx, StrBuf* Target, MacroExp* E, Macro* M, int MultiLine)
+/* Expand a macro into Target. Return the length of the last pp-token in the
+** result of the expansion.
+*/
{
- MacroExp E;
+ unsigned Count = 0; /* Count of identifiers and right parentheses */
+ unsigned Len = 0; /* Length of the last pp-token in the result */
-#if 0
+ /* Disable previous pp-token spacing checking */
+ StrBuf* PrevTok = CurRescanStack->PrevTok;
+ CurRescanStack->PrevTok = 0;
+
+#if DEV_CC65_DEBUG
static unsigned V = 0;
- printf ("Expanding %s(%u)\n", M->Name, ++V);
+ printf ("Expanding (%u) %s\n", ++V, M->Name);
#endif
- /* Initialize our MacroExp structure */
- InitMacroExp (&E);
-
/* Check if this is a function like macro */
- if (M->ArgCount >= 0) {
-
+ if (M->ParamCount >= 0) {
/* Read the actual macro arguments (with the enclosing parentheses) */
- ReadMacroArgs (&E, M, MultiLine);
-
+ Count = ReadMacroArgs (Idx, E, M, MultiLine);
}
- /* Replace macro arguments handling the # and ## operators */
- SubstMacroArgs (&E, M);
+ if ((E->Flags & MES_ERROR) == 0) {
+ /* Replace macro parameters with arguments handling the # and ## operators */
+ Len = SubstMacroArgs (Idx, Target, E, M, &Count);
+ } else {
+ SB_CopyStr (Target, M->Name);
+ }
- /* Forbide repeated expansion of the same macro in use */
- M->Expanding = 1;
- MacroReplacement (&E.Replacement, Target, 0);
- M->Expanding = 0;
+ if (CollCount (&E->Args) > 0) {
+ /* Clear all arguments */
+ ME_ClearArgs (E);
+ }
-#if 0
- printf ("Done with %s(%u)\n", E.M->Name, V--);
+#if DEV_CC65_DEBUG
+ printf ("Expanded (%u) %s to %d ident(s) at %u: %s\n",
+ V--, M->Name, Count, Idx, SB_GetConstBuf (Target));
#endif
- /* Free memory allocated for the macro expansion structure */
- DoneMacroExp (&E);
+ /* Reenable previous pp-token concatenation checking */
+ FreeStrBuf (CurRescanStack->PrevTok);
+ CurRescanStack->PrevTok = PrevTok;
+
+ /* Return the length of the last pp-token in the expansion result */
+ return Len;
}
-static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags)
-/* Scan for and perform macro replacement */
+static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsigned ModeFlags)
+/* Scan for and perform macro replacement. Return the count of identifiers and
+** right parentheses in the replacement result.
+*/
{
- ident Ident;
+ unsigned Count = 0;
+ StrBuf* TmpTarget = NewStrBuf ();
/* Remember the current input and switch to Source */
- StrBuf* OldSource = InitLine (Source);
+ StrBuf* OldSource = InitLine (Source);
+ RescanInputStack RescanStack;
+ RescanInputStack* OldRescanStack = CurRescanStack;
+
+ InitRescanInputStack (&RescanStack);
+ PushRescanLine (&RescanStack, Line, 0);
+ CurRescanStack = &RescanStack;
/* Loop substituting macros */
while (CurC != '\0') {
+ int Skipped = 0;
+ ident Ident;
+
/* If we have an identifier, check if it's a macro */
if (IsSym (Ident)) {
+ /* Check for bad identifier names */
+ if ((ModeFlags & (MSM_MULTILINE | MSM_IN_DIRECTIVE | MSM_IN_ARG_LIST)) != 0 &&
+ (CollCount (&CurRescanStack->Lines) == 1 || CurC == '\0')) {
+ CheckForBadIdent (Ident, IS_Get (&Standard), 0);
+ }
+
if ((ModeFlags & MSM_OP_DEFINED) != 0 && strcmp (Ident, "defined") == 0) {
/* Handle the "defined" operator */
int HaveParen = 0;
+ /* Eat the "defined" operator */
+ ME_RemoveToken (Count, 1, E);
+
SkipWhitespace (0);
if (CurC == '(') {
HaveParen = 1;
NextChar ();
SkipWhitespace (0);
}
+
+ /* Add a space to separate the result if necessary */
+ SeparatePPTok (Target, '0');
+
if (IsSym (Ident)) {
/* Eat the identifier */
+ ME_RemoveToken (Count, 1, E);
SB_AppendChar (Target, IsMacro (Ident) ? '1' : '0');
if (HaveParen) {
SkipWhitespace (0);
@@ -860,6 +2147,8 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags
PPError ("')' expected");
ClearLine ();
} else {
+ /* Eat the right parenthesis */
+ ME_RemoveToken (Count, 1, E);
NextChar ();
}
}
@@ -869,83 +2158,289 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags
SB_AppendChar (Target, '0');
}
} else {
- /* Check if it's an expandable macro */
Macro* M = FindMacro (Ident);
- if (M != 0 && !M->Expanding) {
+
+ /* Check if it's an expandable macro */
+ if (M != 0 && ME_CanExpand (Count, E, M)) {
+ int MultiLine = (ModeFlags & MSM_MULTILINE) != 0;
+ unsigned LastTokLen;
+
/* Check if this is a function-like macro */
- if (M->ArgCount >= 0) {
- int MultiLine = (ModeFlags & MSM_MULTILINE) != 0;
- int Whitespace = SkipWhitespace (MultiLine);
+ if (M->ParamCount >= 0) {
+ int HaveSpace = SkipWhitespace (MultiLine) > 0;
+
+ /* A function-like macro name without an immediately
+ ** following argument list is not subject to expansion.
+ */
if (CurC != '(') {
- /* Function-like macro without an argument list is not replaced */
+ /* No expansion */
+ ++Count;
+
+ /* Add a space to separate the macro name if necessary */
+ SeparatePPTok (Target, M->Name[0]);
SB_AppendStr (Target, M->Name);
- if (Whitespace > 0) {
+
+ /* Keep tracking pp-token lengths */
+ if ((ModeFlags & MSM_IN_ARG_EXPANSION) != 0) {
+ /* Used for concatentation check */
+ if ((E->Flags & MES_FIRST_TOKEN) != 0) {
+ E->Flags |= MES_BEGIN_WITH_IDENT;
+ }
+ ME_SetTokLens (E, strlen (M->Name));
+ }
+
+ /* Since we have already got on hold of the next
+ ** line, we have to reuse it as the next line
+ ** instead of reading a new line from the source.
+ */
+ if (PendingNewLines > 0 && MultiLine) {
+ unsigned I = SB_GetIndex (Line);
+
+ /* There is no way a function-like macro call
+ ** detection could span multiple lines within
+ ** the range of another just expanded macro.
+ */
+ CHECK (CollCount (&CurRescanStack->Lines) == 1);
+
+ /* Revert one newline */
+ --PendingNewLines;
+
+ /* Align indention */
+ while (I > 0) {
+ --I;
+ if (SB_GetBuf (Line)[I] == '\n') {
+ ++I;
+ break;
+ }
+ SB_GetBuf (Line)[I] = ' ';
+ }
+
+ /* Set start index */
+ SB_SetIndex (Line, I);
+
+ /* Add newlines */
+ AddPreLine (Target);
+
+ /* Reuse this line as the next line */
+ ReuseInputLine ();
+
+ /* Quit this loop */
+ break;
+ }
+
+ /* Append back the whitespace */
+ if (HaveSpace) {
SB_AppendChar (Target, ' ');
}
- /* Directives can only be found in an argument list
- ** that spans multiple lines.
- */
- if (MultiLine) {
- if (CurC == '#') {
- /* If we were going to support #pragma in
- ** macro argument list, it would be output
- ** to OLine.
- */
- if (OLine == 0) {
- OLine = Target;
- ParseDirectives (ModeFlags);
- OLine = 0;
- } else {
- ParseDirectives (ModeFlags);
- }
- }
- /* Add the source info to preprocessor output if needed */
- AddPreLine (Target);
- }
- } else {
- /* Function-like macro */
- if (OLine == 0) {
- OLine = Target;
- ExpandMacro (Target, M, MultiLine);
- OLine = 0;
- } else {
- ExpandMacro (Target, M, MultiLine);
- }
+ /* Loop */
+ goto Loop;
}
- } else {
- /* Object-like macro */
- ExpandMacro (Target, M, 0);
}
- } else {
- /* An identifier, keep it */
- SB_AppendStr (Target, Ident);
+
+ /* Either an object-like or function-like macro */
+ MultiLine = MultiLine && M->ParamCount >= 0;
+
+ /* If we were going to support #pragma in macro argument
+ ** list, it would be output to OLine.
+ */
+ if (MultiLine && OLine == 0) {
+ OLine = TmpTarget;
+ LastTokLen = ExpandMacro (Count, TmpTarget, E, M, MultiLine);
+ OLine = 0;
+ } else {
+ LastTokLen = ExpandMacro (Count, TmpTarget, E, M, MultiLine);
+ }
+
+ /* Check for errors in expansion */
+ if ((E->Flags & MES_ERROR) != 0) {
+ break;
+ }
+
+ /* Pop the current line if it is at the end */
+ PopRescanLine ();
+
+ if (SB_GetLen (TmpTarget) > 0) {
+ /* Start rescanning from the temporary result */
+ SB_Reset (TmpTarget);
+ InitLine (TmpTarget);
+ PushRescanLine (CurRescanStack, TmpTarget, LastTokLen);
+
+ /* Add a space before a '#' at the beginning of the line */
+ if (CurC == '#' &&
+ NextC != '#' &&
+ (SB_IsEmpty (Target) || SB_LookAtLast (Target) == '\n')) {
+ SB_AppendChar (Target, ' ');
+ }
+
+ /* Switch the buffers */
+ TmpTarget = NewStrBuf ();
+ } else if (PendingNewLines > 0 && MultiLine) {
+ /* Cancel remaining check for pp-tokens separation
+ ** if there is since ther have been newlines that
+ ** can always separate them.
+ */
+ if (CurRescanStack->PrevTok != 0) {
+ FreeStrBuf (CurRescanStack->PrevTok);
+ CurRescanStack->PrevTok = 0;
+ }
+
+ /* Squeeze whitespace */
+ SkipWhitespace (0);
+
+ /* Add indention to preprocessor output if needed */
+ if (CurC != '\0' && CollCount (&CurRescanStack->Lines) == 1) {
+ /* Add newlines */
+ AddPreLine (Target);
+
+ /* Align indention */
+ AppendIndent (Target, SB_GetIndex (Line));
+ }
+ }
+
+ /* Since we are rescanning, we needn't add the
+ ** count of just replaced identifiers right now.
+ */
+ continue;
+ }
+
+ /* An unexpandable identifier. Keep it. */
+ ++Count;
+
+ /* Add a space to separate the macro name if necessary */
+ SeparatePPTok (Target, Ident[0]);
+ SB_AppendStr (Target, Ident);
+
+ /* Keep tracking pp-token lengths */
+ if ((ModeFlags & MSM_IN_ARG_EXPANSION) != 0) {
+ /* Used for concatentation check */
+ if ((E->Flags & MES_FIRST_TOKEN) != 0) {
+ E->Flags |= MES_BEGIN_WITH_IDENT;
+ }
+ ME_SetTokLens (E, strlen (Ident));
}
}
} else {
+ unsigned LastLen;
+
+ /* Add a space to separate the macro name if necessary */
+ SeparatePPTok (Target, CurC);
+
+ LastLen = SB_GetLen (Target);
+
if ((ModeFlags & MSM_TOK_HEADER) != 0 && (CurC == '<' || CurC == '\"')) {
CopyHeaderNameToken (Target);
- } else if (IsQuote (CurC)) {
+ } else if (IsPPNumber (CurC, NextC)) {
+ CopyPPNumber (Target);
+ } else if (IsQuotedString ()) {
CopyQuotedString (Target);
} else {
- int Whitespace = SkipWhitespace (0);
- if (Whitespace) {
- SB_AppendChar (Target, ' ');
+ /* We want to squeeze whitespace until the end of the current
+ ** input line, so we have to deal with such cases specially.
+ */
+ if (CollCount (&CurRescanStack->Lines) > 1) {
+ RescanInputStack* RIS = CurRescanStack;
+
+ /* Temporarily disable input popping */
+ CurRescanStack = 0;
+ Skipped = SkipWhitespace (0);
+ CurRescanStack = RIS;
+
+ if (CurC == '\0') {
+ /* Now we are at the end of the input line */
+ goto Loop;
+ }
} else {
- SB_AppendChar (Target, CurC);
- NextChar ();
+ Skipped = SkipWhitespace (0);
+ }
+
+ /* Punctuators must be checked after whitespace since comments
+ ** introducers may be misinterpreted as division operators.
+ */
+ if (!Skipped) {
+ if (GetPunc (Ident)) {
+ if (Ident[0] == ')') {
+ /* Count right parens */
+ ++Count;
+ }
+ SB_AppendStr (Target, Ident);
+
+ /* If an identifier follows immediately, it could be a macro
+ ** expanded later that occasionally need a space to separate.
+ */
+ if (IsIdent (CurC)) {
+ /* Memorize the previous pp-token and check it later */
+ LazyCheckNextPPTok (Target, strlen (Ident));
+ }
+ } else {
+ SB_AppendChar (Target, CurC);
+ NextChar ();
+
+ /* Don't count this character */
+ ++LastLen;
+ }
}
}
+
+ /* Keep tracking pp-token lengths */
+ if ((ModeFlags & MSM_IN_ARG_EXPANSION) != 0) {
+ ME_SetTokLens (E, SB_GetLen (Target) - LastLen);
+ }
+ }
+
+Loop:
+ /* Switch back to the previous input stream if we have finished
+ ** rescanning the current one.
+ */
+ if (CurC == '\0' && CollCount (&CurRescanStack->Lines) > 1) {
+ /* Check for rescan sequence end and pp-token pasting */
+ Skipped = SkipWhitespace (0) || Skipped;
+
+ /* Add indention to preprocessor output if needed */
+ if (CurC != '\0' &&
+ PendingNewLines > 0 &&
+ (ModeFlags & MSM_MULTILINE) != 0 &&
+ CollCount (&CurRescanStack->Lines) == 1) {
+ /* Add newlines */
+ AddPreLine (Target);
+
+ /* Align indention */
+ AppendIndent (Target, SB_GetIndex (Line));
+ Skipped = 0;
+ }
+ }
+
+ /* Append a space if there hasn't been one */
+ if (Skipped && !IsSpace (SB_LookAtLast (Target))) {
+ SB_AppendChar (Target, ' ');
}
}
+ /* Append the remaining result */
+ SB_Append (Target, TmpTarget);
+
+ /* Done with the temporary buffer */
+ SB_Done (TmpTarget);
+
/* Drop whitespace at the end */
if (IsBlank (SB_LookAtLast (Target))) {
SB_Drop (Target, 1);
}
+ /* Sanity check */
+ if ((E->Flags & MES_ERROR) == 0) {
+ CHECK (CollCount (&CurRescanStack->Lines) == 1);
+ }
+
+ /* Done with the current input stack */
+ DoneRescanInputStack (CurRescanStack);
+ CurRescanStack = OldRescanStack;
+
/* Switch back the input */
InitLine (OldSource);
+
+ /* Return the count of identifiers and right parentheses */
+ return Count;
}
@@ -956,41 +2451,137 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags
+static int ParseMacroReplacement (StrBuf* Source, Macro* M)
+/* Check correctness of macro definition while squeezing old and new style
+** comments and other non-newline whitespace sequences. Return 1 on success
+** or 0 on failure.
+*/
+{
+ /* Switch to the new input source */
+ StrBuf* OldSource = InitLine (Source);
+ int HasWhiteSpace = 0;
+ unsigned Len;
+ ident Ident;
+ int Std = IS_Get (&Standard);
+
+ /* Skip whitespace before the macro replacement */
+ SkipWhitespace (0);
+
+ /* Check for ## at start */
+ if (CurC == '#' && NextC == '#') {
+ /* Diagnose and bail out */
+ PPError ("'##' cannot appear at start of macro expansion");
+ goto Error_Handler;
+ }
+
+ /* Loop removing ws and comments */
+ while (CurC != '\0') {
+ if (HasWhiteSpace) {
+ SB_AppendChar (&M->Replacement, ' ');
+ } else if (IsQuotedString ()) {
+ CopyQuotedString (&M->Replacement);
+ } else if (IsSym (Ident)) {
+ CheckForBadIdent (Ident, Std, M);
+ SB_AppendStr (&M->Replacement, Ident);
+ } else {
+ if (M->ParamCount >= 0 && GetPunc (Ident)) {
+ Len = strlen (Ident);
+ /* Check for # */
+ if (Len == 1 && Ident[0] == '#') {
+ HasWhiteSpace = SkipWhitespace (0);
+
+ /* Check next pp-token */
+ if (!IsSym (Ident) || FindMacroParam (M, Ident) < 0) {
+ PPError ("'#' is not followed by a macro parameter");
+ goto Error_Handler;
+ }
+
+ /* Make the replacement */
+ SB_AppendChar (&M->Replacement, '#');
+ if (HasWhiteSpace) {
+ SB_AppendChar (&M->Replacement, ' ');
+ }
+ SB_AppendStr (&M->Replacement, Ident);
+ } else {
+ SB_AppendBuf (&M->Replacement, Ident, Len);
+ }
+ } else {
+ SB_AppendChar (&M->Replacement, CurC);
+ NextChar ();
+ }
+ }
+
+ HasWhiteSpace = SkipWhitespace (0);
+ }
+
+ /* Check for ## at end */
+ Len = SB_GetLen (&M->Replacement);
+ if (Len >= 2) {
+ if (SB_LookAt (&M->Replacement, Len - 1) == '#' &&
+ SB_LookAt (&M->Replacement, Len - 2) == '#') {
+ /* Diagnose and bail out */
+ PPError ("'##' cannot appear at end of macro expansion");
+ goto Error_Handler;
+ }
+ }
+
+ /* Terminate the new input line */
+ SB_Terminate (&M->Replacement);
+
+ /* Switch back to the old source */
+ InitLine (OldSource);
+
+ /* Success */
+ return 1;
+
+Error_Handler:
+
+ /* Switch back to the old source */
+ InitLine (OldSource);
+
+ /* Failure */
+ return 0;
+}
+
+
+
static void DoDefine (void)
/* Process #define directive */
{
ident Ident;
- Macro* M;
+ Macro* M = 0;
Macro* Existing;
- int C89;
- unsigned Len;
+ int Std;
/* Read the macro name */
SkipWhitespace (0);
if (!MacName (Ident)) {
- return;
+ goto Error_Handler;
}
- /* Remember if we're in C89 mode */
- C89 = (IS_Get (&Standard) == STD_C89);
+ /* Remember the language standard we are in */
+ Std = IS_Get (&Standard);
/* Check for forbidden macro names */
if (strcmp (Ident, "defined") == 0) {
PPError ("'%s' cannot be used as a macro name", Ident);
- return;
+ goto Error_Handler;
}
+ /* Check for and warn on special identifiers */
+ CheckForBadIdent (Ident, Std, 0);
+
/* Create a new macro definition */
M = NewMacro (Ident);
- /* Check if this is a function like macro */
+ /* Check if this is a function-like macro */
if (CurC == '(') {
/* Skip the left paren */
NextChar ();
- /* Set the marker that this is a function like macro */
- M->ArgCount = 0;
+ /* Set the marker that this is a function-like macro */
+ M->ParamCount = 0;
/* Read the formal parameter list */
while (1) {
@@ -1004,42 +2595,37 @@ static void DoDefine (void)
/* The next token must be either an identifier, or - if not in
** C89 mode - the ellipsis.
*/
- if (!C89 && CurC == '.') {
+ if (Std >= STD_C99 && CurC == '.') {
/* Ellipsis */
NextChar ();
if (CurC != '.' || NextC != '.') {
PPError ("'...' expected");
- ClearLine ();
- FreeMacro (M);
- return;
+ goto Error_Handler;
}
NextChar ();
NextChar ();
/* Remember that the macro is variadic and use __VA_ARGS__ as
- ** the argument name.
+ ** the parameter name.
*/
- AddMacroArg (M, "__VA_ARGS__");
+ AddMacroParam (M, "__VA_ARGS__");
M->Variadic = 1;
} else {
- /* Must be macro argument name */
+ /* Must be macro parameter name */
if (MacName (Ident) == 0) {
- return;
+ goto Error_Handler;
}
- /* __VA_ARGS__ is only allowed in post-C89 mode */
- if (!C89 && strcmp (Ident, "__VA_ARGS__") == 0) {
- PPWarning ("'__VA_ARGS__' can only appear in the expansion "
- "of a C99 variadic macro");
- }
+ /* Check for and warn on special identifiers */
+ CheckForBadIdent (Ident, Std, 0);
- /* Add the macro argument */
- AddMacroArg (M, Ident);
+ /* Add the macro parameter */
+ AddMacroParam (M, Ident);
}
/* If we had an ellipsis, or the next char is not a comma, we've
- ** reached the end of the macro argument list.
+ ** reached the end of the macro parameter list.
*/
SkipWhitespace (0);
if (M->Variadic || CurC != ',') {
@@ -1051,47 +2637,22 @@ static void DoDefine (void)
/* Check for a right paren and eat it if we find one */
if (CurC != ')') {
PPError ("')' expected for macro definition");
- ClearLine ();
- FreeMacro (M);
- return;
+ goto Error_Handler;
}
NextChar ();
}
- /* Skip whitespace before the macro replacement */
- SkipWhitespace (0);
-
/* Remove whitespace and comments from the line, store the preprocessed
** line into the macro replacement buffer.
*/
- TranslationPhase3 (Line, &M->Replacement);
-
- /* Remove whitespace from the end of the line */
- while (IsSpace (SB_LookAtLast (&M->Replacement))) {
- SB_Drop (&M->Replacement, 1);
+ if (ParseMacroReplacement (Line, M) == 0) {
+ goto Error_Handler;
}
+
#if 0
printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement));
#endif
- /* Check for ## at start or end */
- Len = SB_GetLen (&M->Replacement);
- if (Len >= 2) {
- if (SB_LookAt (&M->Replacement, 0) == '#' &&
- SB_LookAt (&M->Replacement, 1) == '#') {
- /* Diagnose and bail out */
- PPError ("'##' cannot appear at start of macro expansion");
- FreeMacro (M);
- return;
- } else if (SB_LookAt (&M->Replacement, Len - 1) == '#' &&
- SB_LookAt (&M->Replacement, Len - 2) == '#') {
- /* Diagnose and bail out */
- PPError ("'##' cannot appear at end of macro expansion");
- FreeMacro (M);
- return;
- }
- }
-
/* Get an existing macro definition with this name */
Existing = FindMacro (M->Name);
@@ -1108,6 +2669,18 @@ static void DoDefine (void)
/* Insert the new macro into the macro table */
InsertMacro (M);
+
+ /* Success */
+ return;
+
+Error_Handler:
+
+ /* Cleanup */
+ ClearLine ();
+
+ if (M != 0) {
+ FreeMacro (M);
+ }
}
@@ -1220,6 +2793,7 @@ static int DoIfDef (int skip, int flag)
SkipWhitespace (0);
if (MacName (Ident)) {
+ CheckForBadIdent (Ident, IS_Get (&Standard), 0);
Value = IsMacro (Ident);
/* Check for extra tokens */
CheckExtraTokens (flag ? "ifdef" : "ifndef");
@@ -1379,7 +2953,7 @@ static void DoLine (void)
/* #line actually sets the line number of the next line */
if (LineNum > 0) {
- SetCurrentLine (LineNum - 1);
+ SetCurrentLineNum (LineNum - 1);
/* Check for extra tokens at the end */
CheckExtraTokens ("line");
}
@@ -1426,6 +3000,7 @@ static void DoUndef (void)
SkipWhitespace (0);
if (MacName (Ident)) {
+ CheckForBadIdent (Ident, IS_Get (&Standard), 0);
UndefineMacro (Ident);
}
/* Check for extra tokens */
@@ -1582,6 +3157,7 @@ static int ParseDirectives (unsigned ModeFlags)
if (!PPSkip) {
if ((ModeFlags & MSM_IN_ARG_LIST) == 0) {
DoPragma ();
+ return Whitespace;
} else {
PPError ("Embedded #pragma directive within macro arguments is unsupported");
}
@@ -1624,7 +3200,7 @@ static int ParseDirectives (unsigned ModeFlags)
Whitespace = SkipWhitespace (0) || Whitespace;
}
- return Whitespace != 0;
+ return Whitespace;
}
@@ -1640,11 +3216,11 @@ void HandleSpecialMacro (Macro* M, const char* Name)
SB_Printf (&M->Replacement, "%u", GetCurrentCounter ());
} else if (strcmp (Name, "__LINE__") == 0) {
/* Replace __LINE__ with the current line number */
- SB_Printf (&M->Replacement, "%u", GetCurrentLine ());
+ SB_Printf (&M->Replacement, "%u", GetCurrentLineNum ());
} else if (strcmp (Name, "__FILE__") == 0) {
/* Replace __FILE__ with the current filename */
StrBuf B = AUTO_STRBUF_INITIALIZER;
- SB_InitFromString (&B, GetCurrentFile ());
+ SB_InitFromString (&B, GetCurrentFilename ());
SB_Clear (&M->Replacement);
Stringize (&B, &M->Replacement);
SB_Done (&B);
@@ -1688,7 +3264,7 @@ static void TranslationPhase3 (StrBuf* Source, StrBuf* Target)
}
if (HasWhiteSpace) {
SB_AppendChar (Target, ' ');
- } else if (IsQuote (CurC)) {
+ } else if (IsQuotedString ()) {
CopyQuotedString (Target);
} else {
SB_AppendChar (Target, CurC);
@@ -1710,16 +3286,12 @@ static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFl
** whitespace and comments, then do macro replacement.
*/
{
- int OldIndex = SB_GetIndex (Source);
MacroExp E;
SkipWhitespace (0);
InitMacroExp (&E);
- MacroReplacement (Source, Target, ModeFlags | MSM_IN_DIRECTIVE);
+ ReplaceMacros (Source, Target, &E, ModeFlags | MSM_IN_DIRECTIVE);
DoneMacroExp (&E);
-
- /* Restore the source input index */
- SB_SetIndex (Source, OldIndex);
}
@@ -1727,6 +3299,8 @@ static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFl
void Preprocess (void)
/* Preprocess lines count of which is affected by directives */
{
+ MacroExp E;
+
SB_Clear (PLine);
/* Add the source info to preprocessor output if needed */
@@ -1741,10 +3315,14 @@ void Preprocess (void)
AddPreLine (PLine);
/* Add leading whitespace to prettify preprocessor output */
- AppendIndent (PLine, SB_GetIndex (Line));
+ if (CurC != '\0') {
+ AppendIndent (PLine, SB_GetIndex (Line));
+ }
/* Expand macros if any */
- MacroReplacement (Line, PLine, MSM_MULTILINE);
+ InitMacroExp (&E);
+ ReplaceMacros (Line, PLine, &E, MSM_MULTILINE);
+ DoneMacroExp (&E);
/* Add the source info to preprocessor output if needed */
AddPreLine (PLine);
@@ -1754,7 +3332,7 @@ void Preprocess (void)
PLine = InitLine (PLine);
if (Verbosity > 1 && SB_NotEmpty (Line)) {
- printf ("%s:%u: %.*s\n", GetCurrentFile (), GetCurrentLine (),
+ printf ("%s:%u: %.*s\n", GetCurrentFilename (), GetCurrentLineNum (),
(int) SB_GetLen (Line), SB_GetConstBuf (Line));
}
@@ -1792,6 +3370,14 @@ void SetPPIfStack (PPIfStack* Stack)
+void ContinueLine (void)
+/* Continue the current line ended with a '\\' */
+{
+ ++ContinuedLines;
+}
+
+
+
void PreprocessBegin (void)
/* Initialize preprocessor with current file */
{
@@ -1800,6 +3386,9 @@ void PreprocessBegin (void)
/* Remember to update source file location in preprocess-only mode */
FileChanged = 1;
+
+ /* Enable diagnostics on new style comments in C89 mode */
+ AllowNewComments = 0;
}
diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h
index a4619e545..e2a1b073c 100644
--- a/src/cc65/preproc.h
+++ b/src/cc65/preproc.h
@@ -36,17 +36,7 @@
#ifndef PREPROC_H
#define PREPROC_H
-
-
-/*****************************************************************************/
-/* Forwards */
-/*****************************************************************************/
-
-
-
-typedef struct Macro Macro;
-
-
+#include "macrotab.h"
/*****************************************************************************/
/* Data */
@@ -78,6 +68,9 @@ void Preprocess (void);
void SetPPIfStack (PPIfStack* Stack);
/* Specify which PP #if stack to use */
+void ContinueLine (void);
+/* Continue the current line ended with a '\\' */
+
void PreprocessBegin (void);
/* Initialize preprocessor with current file */
diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c
index 09dd8fef8..36fd1301b 100644
--- a/src/cc65/scanner.c
+++ b/src/cc65/scanner.c
@@ -235,10 +235,20 @@ void SymName (char* S)
+int IsWideQuoted (char First, char Second)
+/* Return 1 if the two successive characters indicate a wide string literal or
+** a wide char constant, otherwise return 0.
+*/
+{
+ return First == 'L' && IsQuote(Second);
+}
+
+
+
int IsSym (char* S)
/* If a symbol follows, read it and return 1, otherwise return 0 */
{
- if (IsIdent (CurC)) {
+ if (IsIdent (CurC) && !IsWideQuoted (CurC, NextC)) {
SymName (S);
return 1;
} else {
@@ -633,7 +643,7 @@ static void NumericConst (void)
if (IVal <= 0xFFFF &&
(Types & IT_UINT) == 0 &&
(WarnTypes & IT_LONG) != 0) {
- Warning ("Integer constant is long");
+ Warning ("Integer constant implies signed long");
}
}
if (IVal > 0xFFFF) {
@@ -650,7 +660,7 @@ static void NumericConst (void)
** a preceding unary op or when it is used in constant calculation.
*/
if (WarnTypes & IT_ULONG) {
- Warning ("Integer constant is unsigned long");
+ Warning ("Integer constant implies unsigned long");
}
}
diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h
index 7a67b10ed..338ad6a65 100644
--- a/src/cc65/scanner.h
+++ b/src/cc65/scanner.h
@@ -282,6 +282,11 @@ void SymName (char* S);
** least of size MAX_IDENTLEN+1.
*/
+int IsWideQuoted (char First, char Second);
+/* Return 1 if the two successive characters indicate a wide string literal or
+** a wide char constant, otherwise return 0.
+*/
+
int IsSym (char* S);
/* If a symbol follows, read it and return 1, otherwise return 0 */
diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c
index eb879a475..b8fb70434 100644
--- a/src/cc65/shiftexpr.c
+++ b/src/cc65/shiftexpr.c
@@ -139,20 +139,34 @@ void ShiftExpr (struct ExprDesc* Expr)
/* Remove the code that pushes the rhs onto the stack. */
RemoveCode (&Mark2);
- /* If the shift count is greater or equal than the bit count of
- ** the operand, the behaviour is undefined according to the
- ** standard.
+ /* If the shift count is greater than or equal to the width of the
+ ** promoted left operand, the behaviour is undefined according to
+ ** the standard.
*/
- if (Expr2.IVal < 0) {
+ if (!ED_IsUneval (Expr)) {
+ if (Expr2.IVal < 0) {
+ Warning ("Negative shift count %ld treated as %u for %s",
+ Expr2.IVal,
+ (unsigned)Expr2.IVal & (ExprBits - 1),
+ GetBasicTypeName (ResultType));
+ } else if (Expr2.IVal >= (long) ExprBits) {
+ Warning ("Shift count %ld >= width of %s treated as %u",
+ Expr2.IVal,
+ GetBasicTypeName (ResultType),
+ (unsigned)Expr2.IVal & (ExprBits - 1));
+ }
+ }
- Warning ("Shift count '%ld' is negative", Expr2.IVal);
- Expr2.IVal &= ExprBits - 1;
-
- } else if (Expr2.IVal >= (long) ExprBits) {
-
- Warning ("Shift count '%ld' >= width of type", Expr2.IVal);
- Expr2.IVal &= ExprBits - 1;
+ /* Here we simply "wrap" the shift count around the width */
+ Expr2.IVal &= ExprBits - 1;
+ /* Additional check for bit-fields */
+ if (IsTypeBitField (Expr->Type) &&
+ Tok == TOK_SHR &&
+ Expr2.IVal >= (long) Expr->Type->A.B.Width) {
+ if (!ED_IsUneval (Expr)) {
+ Warning ("Right-shift count %ld >= width of bit-field", Expr2.IVal);
+ }
}
/* If the shift count is zero, nothing happens. If the left hand
@@ -173,7 +187,7 @@ void ShiftExpr (struct ExprDesc* Expr)
}
/* Limit the calculated value to the range of its type */
- LimitExprValue (Expr);
+ LimitExprValue (Expr, 1);
}
/* Result is already got, remove the generated code */
diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c
index cc790c931..30ebe7dd8 100644
--- a/src/cc65/symentry.c
+++ b/src/cc65/symentry.c
@@ -40,6 +40,7 @@
/* cc65 */
#include "anonname.h"
+#include "asmlabel.h"
#include "declare.h"
#include "error.h"
#include "symentry.h"
@@ -65,13 +66,12 @@ SymEntry* NewSymEntry (const char* Name, unsigned Flags)
E->NextHash = 0;
E->PrevSym = 0;
E->NextSym = 0;
- E->Link = 0;
E->Owner = 0;
E->Flags = Flags;
E->Type = 0;
E->Attr = 0;
E->AsmName = 0;
- E->V.BssName = 0;
+ memset (&E->V, 0, sizeof (E->V));
memcpy (E->Name, Name, Len+1);
/* Return the new entry */
@@ -230,8 +230,8 @@ const DeclAttr* SymGetAttr (const SymEntry* Sym, DeclAttrType AttrType)
-void SymUseAttr (SymEntry* Sym, struct Declaration* D)
-/* Use the attributes from the declaration for this symbol */
+void SymUseAttr (SymEntry* Sym, struct Declarator* D)
+/* Use the attributes from the declarator for this symbol */
{
/* We cannot specify attributes twice */
if ((Sym->Flags & SC_HAVEATTR) != 0) {
@@ -250,7 +250,9 @@ void SymUseAttr (SymEntry* Sym, struct Declaration* D)
void SymSetAsmName (SymEntry* Sym)
-/* Set the assembler name for an external symbol from the name of the symbol */
+/* Set the assembler name for an external symbol from the name of the symbol.
+** The symbol must have no assembler name set yet.
+*/
{
unsigned Len;
@@ -266,7 +268,7 @@ void SymSetAsmName (SymEntry* Sym)
-void CvtRegVarToAuto (SymEntry* Sym)
+void SymCvtRegVarToAuto (SymEntry* Sym)
/* Convert a register variable to an auto variable */
{
/* Change the storage class */
@@ -278,59 +280,26 @@ void CvtRegVarToAuto (SymEntry* Sym)
-SymEntry* GetSymType (const Type* T)
-/* Get the symbol entry of the enum/struct/union type
-** Return 0 if it is not an enum/struct/union.
-*/
-{
- if ((IsClassStruct (T) || IsTypeEnum (T))) {
- return T->A.S;
- }
- return 0;
-}
-
-
-
-const char* GetSymTypeName (const Type* T)
-/* Return a name string of the type or the symbol name if it is an ESU type.
-** Note: This may use a static buffer that could be overwritten by other calls.
-*/
-{
- static char TypeName [IDENTSIZE + 16];
- SymEntry* Sym;
-
- Sym = GetSymType (T);
- if (Sym == 0) {
- return GetBasicTypeName (T);
- }
- sprintf (TypeName, "%s %s", GetBasicTypeName (T),
- Sym->Name[0] != '\0' ? Sym->Name : "");
-
- return TypeName;
-}
-
-
-
-void ChangeSymType (SymEntry* Entry, const Type* T)
+void SymChangeType (SymEntry* Sym, const Type* T)
/* Change the type of the given symbol */
{
- TypeFree (Entry->Type);
- Entry->Type = TypeDup (T);
+ TypeFree (Sym->Type);
+ Sym->Type = TypeDup (T);
}
-void ChangeAsmName (SymEntry* Entry, const char* NewAsmName)
+void SymChangeAsmName (SymEntry* Sym, const char* NewAsmName)
/* Change the assembler name of the symbol */
{
- xfree (Entry->AsmName);
- Entry->AsmName = xstrdup (NewAsmName);
+ xfree (Sym->AsmName);
+ Sym->AsmName = xstrdup (NewAsmName);
}
-int HasAnonName (const SymEntry* Entry)
+int SymHasAnonName (const SymEntry* Sym)
/* Return true if the symbol entry has an anonymous name */
{
- return IsAnonName (Entry->Name);
+ return IsAnonName (Sym->Name);
}
diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h
index bcf586510..5ebd30a75 100644
--- a/src/cc65/symentry.h
+++ b/src/cc65/symentry.h
@@ -105,8 +105,8 @@ struct CodeEntry;
#define SC_SPADJUSTMENT 0x400000U
#define SC_GOTO_IND 0x800000U /* Indirect goto */
-#define SC_ALIAS 0x01000000U /* Alias of anonymous field */
-#define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious */
+#define SC_ALIAS 0x01000000U /* Alias of global or anonymous field */
+#define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious (for error recovery) */
#define SC_HAVEFAM 0x04000000U /* Type has a Flexible Array Member */
@@ -128,7 +128,6 @@ struct SymEntry {
SymEntry* NextHash; /* Next entry in hash list */
SymEntry* PrevSym; /* Previous symbol in dl list */
SymEntry* NextSym; /* Next symbol double linked list */
- SymEntry* Link; /* General purpose single linked list */
struct SymTable* Owner; /* Symbol table the symbol is in */
unsigned Flags; /* Symbol flags */
Type* Type; /* Symbol type */
@@ -138,27 +137,9 @@ struct SymEntry {
/* Data that differs for the different symbol types */
union {
- /* Offset for locals or struct members */
+ /* Offset for locals */
int Offs;
- /* Data for anonymous struct or union members */
- struct {
- int Offs; /* Byte offset into struct */
- unsigned ANumber; /* Numeric ID */
- SymEntry* Field; /* The real field aliased */
- } A;
-
-
- /* Label name for static symbols */
- struct {
- unsigned Label;
- Collection *DefsOrRefs;
- struct CodeEntry *IndJumpFrom;
- } L;
-
- /* Value of SP adjustment needed after forward 'goto' */
- unsigned short SPAdjustment;
-
/* Register bank offset and offset of the saved copy on stack for
** register variables.
*/
@@ -167,32 +148,50 @@ struct SymEntry {
int SaveOffs;
} R;
- /* Value for constants (including enums) */
+ /* Segment name for tentantive global definitions */
+ const char* BssName;
+
+ /* Value for integer constants (including enumerators) */
long ConstVal;
- /* Data for structs/unions */
- struct {
- struct SymTable* SymTab; /* Member symbol table */
- unsigned Size; /* Size of the union/struct */
- unsigned ACount; /* Count of anonymous fields */
- } S;
-
- /* Data for enums */
- struct {
- struct SymTable* SymTab; /* Member symbol table */
- const Type* Type; /* Underlying type */
- } E;
-
/* Data for functions */
struct {
struct Segments* Seg; /* Segments for this function */
struct LiteralPool* LitPool; /* Literal pool for this function */
} F;
- /* Segment name for tentantive global definitions */
- const char* BssName;
+ /* Label name for static symbols */
+ struct {
+ unsigned Label;
+ Collection *DefsOrRefs;
+ struct CodeEntry *IndJumpFrom;
+ } L;
+
+ /* Value of SP adjustment needed after forward 'goto' */
+ unsigned short SPAdjustment;
+
+ /* Data for anonymous struct or union members */
+ struct {
+ int Offs; /* Byte offset into struct */
+ unsigned ANumber; /* Numeric ID */
+ SymEntry* Field; /* The real field aliased */
+ } A;
+
+ /* Data for structs/unions tags */
+ struct {
+ struct SymTable* SymTab; /* Member symbol table */
+ unsigned Size; /* Size of the union/struct */
+ unsigned ACount; /* Count of anonymous fields */
+ } S;
+
+ /* Data for enums tags */
+ struct {
+ struct SymTable* SymTab; /* Member symbol table */
+ const Type* Type; /* Underlying type */
+ } E;
+
} V;
- char Name[1]; /* Name, dynamically allocated */
+ char Name[1]; /* Name, dynamically allocated */
};
@@ -299,32 +298,24 @@ INLINE int SymHasAttr (const SymEntry* Sym, DeclAttrType A)
# define SymHasAttr(Sym, A) (SymGetAttr (Sym, A) != 0)
#endif
-void SymUseAttr (SymEntry* Sym, struct Declaration* D);
-/* Use the attributes from the declaration for this symbol */
+void SymUseAttr (SymEntry* Sym, struct Declarator* D);
+/* Use the attributes from the declarator for this symbol */
void SymSetAsmName (SymEntry* Sym);
-/* Set the assembler name for an external symbol from the name of the symbol */
+/* Set the assembler name for an external symbol from the name of the symbol.
+** The symbol must have no assembler name set yet.
+*/
-void CvtRegVarToAuto (SymEntry* Sym);
+void SymCvtRegVarToAuto (SymEntry* Sym);
/* Convert a register variable to an auto variable */
-SymEntry* GetSymType (const Type* T);
-/* Get the symbol entry of the enum/struct/union type
-** Return 0 if it is not an enum/struct/union.
-*/
-
-const char* GetSymTypeName (const Type* T);
-/* Return a name string of the type or the symbol name if it is an ESU type.
-** Note: This may use a static buffer that could be overwritten by other calls.
-*/
-
-void ChangeSymType (SymEntry* Entry, const Type* T);
+void SymChangeType (SymEntry* Sym, const Type* T);
/* Change the type of the given symbol */
-void ChangeAsmName (SymEntry* Entry, const char* NewAsmName);
+void SymChangeAsmName (SymEntry* Sym, const char* NewAsmName);
/* Change the assembler name of the symbol */
-int HasAnonName (const SymEntry* Entry);
+int SymHasAnonName (const SymEntry* Sym);
/* Return true if the symbol entry has an anonymous name */
diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c
index aa4a9a44a..961f36046 100644
--- a/src/cc65/symtab.c
+++ b/src/cc65/symtab.c
@@ -567,6 +567,11 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
/* Try to find the symbol in this table */
SymEntry* E = FindSymInTable (Tab, Name, Hash);
+ while (E != 0 && (E->Flags & SC_ALIAS) == SC_ALIAS) {
+ /* Get the aliased entry */
+ E = E->V.A.Field;
+ }
+
/* Bail out if we found it */
if (E != 0) {
return E;
@@ -620,8 +625,8 @@ SymEntry FindStructField (const Type* T, const char* Name)
** value, or an empty entry struct if the field is not found.
*/
{
- SymEntry* Entry = 0;
- SymEntry Field;
+ SymEntry* Field = 0;
+ SymEntry Res;
int Offs = 0;
/* The given type may actually be a pointer to struct/union */
@@ -632,35 +637,35 @@ SymEntry FindStructField (const Type* T, const char* Name)
/* Only structs/unions have struct/union fields... */
if (IsClassStruct (T)) {
- /* Get a pointer to the struct/union type */
- const SymEntry* Struct = GetESUSymEntry (T);
- CHECK (Struct != 0);
+ /* Get a pointer to the struct/union tag */
+ const SymEntry* TagSym = GetESUTagSym (T);
+ CHECK (TagSym != 0);
/* Now search in the struct/union symbol table. Beware: The table may
** not exist.
*/
- if (Struct->V.S.SymTab) {
- Entry = FindSymInTable (Struct->V.S.SymTab, Name, HashStr (Name));
+ if (TagSym->V.S.SymTab) {
+ Field = FindSymInTable (TagSym->V.S.SymTab, Name, HashStr (Name));
- if (Entry != 0) {
- Offs = Entry->V.Offs;
+ if (Field != 0) {
+ Offs = Field->V.Offs;
}
- while (Entry != 0 && (Entry->Flags & SC_ALIAS) == SC_ALIAS) {
+ while (Field != 0 && (Field->Flags & SC_ALIAS) == SC_ALIAS) {
/* Get the real field */
- Entry = Entry->V.A.Field;
+ Field = Field->V.A.Field;
}
}
}
- if (Entry != 0) {
- Field = *Entry;
- Field.V.Offs = Offs;
+ if (Field != 0) {
+ Res = *Field;
+ Res.V.Offs = Offs;
} else {
- memset (&Field, 0, sizeof(SymEntry));
+ memset (&Res, 0, sizeof(SymEntry));
}
- return Field;
+ return Res;
}
@@ -684,15 +689,15 @@ static int IsDistinctRedef (const Type* lhst, const Type* rhst, typecmpcode_t Co
}
-static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags)
+static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags)
/* Check and handle redefinition of existing symbols.
** Complete array sizes and function descriptors as well.
** Return true if there *is* an error.
*/
{
/* Get the type info of the existing symbol */
- Type* E_Type = Entry->Type;
- unsigned E_SCType = Entry->Flags & SC_TYPEMASK;
+ Type* E_Type = Sym->Type;
+ unsigned E_SCType = Sym->Flags & SC_TYPEMASK;
unsigned SCType = Flags & SC_TYPEMASK;
/* Some symbols may be redeclared if certain requirements are met */
@@ -701,15 +706,16 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
/* Existing typedefs cannot be redeclared as anything different */
if (SCType == SC_TYPEDEF) {
if (IsDistinctRedef (E_Type, T, TC_IDENTICAL, TCF_MASK_QUAL)) {
- Error ("Conflicting types for typedef '%s'", Entry->Name);
- Entry = 0;
+ Error ("Conflicting types for typedef '%s'", Sym->Name);
+ Note ("'%s' vs '%s'", GetFullTypeName (T), GetFullTypeName (E_Type));
+ Sym = 0;
}
} else {
- Error ("Redefinition of typedef '%s' as different kind of symbol", Entry->Name);
- Entry = 0;
+ Error ("Redefinition of typedef '%s' as different kind of symbol", Sym->Name);
+ Sym = 0;
}
- } else if ((Entry->Flags & SC_FUNC) == SC_FUNC) {
+ } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) {
/* In case of a function, use the new type descriptor, since it
** contains pointers to the new symbol tables that are needed if
@@ -720,27 +726,27 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
if (IsTypeFunc (T)) {
/* Check for duplicate function definitions */
- if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) {
+ if (SymIsDef (Sym) && (Flags & SC_DEF) == SC_DEF) {
Error ("Body for function '%s' has already been defined",
- Entry->Name);
- Entry = 0;
+ Sym->Name);
+ Sym = 0;
} else {
/* New type must be compatible with the composite prototype */
if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) {
- Error ("Conflicting function types for '%s'", Entry->Name);
- TypeCompatibilityDiagnostic (T, E_Type, 0, "'%s' vs '%s'");
- Entry = 0;
+ Error ("Conflicting function types for '%s'", Sym->Name);
+ Note ("'%s' vs '%s'", GetFullTypeName (T), GetFullTypeName (E_Type));
+ Sym = 0;
} else {
/* Refine the existing composite prototype with this new
** one.
*/
- RefineFuncDesc (Entry->Type, T);
+ RefineFuncDesc (Sym->Type, T);
}
}
} else {
- Error ("Redefinition of function '%s' as different kind of symbol", Entry->Name);
- Entry = 0;
+ Error ("Redefinition of function '%s' as different kind of symbol", Sym->Name);
+ Sym = 0;
}
} else {
@@ -759,8 +765,9 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
if ((Size != UNSPECIFIED && ESize != UNSPECIFIED && Size != ESize) ||
IsDistinctRedef (E_Type + 1, T + 1, TC_IDENTICAL, TCF_MASK_QUAL)) {
/* Conflicting element types */
- Error ("Conflicting array types for '%s[]'", Entry->Name);
- Entry = 0;
+ Error ("Conflicting array types for '%s[]'", Sym->Name);
+ Note ("'%s' vs '%s'", GetFullTypeName (T), GetFullTypeName (E_Type));
+ Sym = 0;
} else {
/* Check if we have a size in the existing definition */
if (ESize == UNSPECIFIED) {
@@ -773,24 +780,25 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
/* New type must be equivalent */
if (SCType != E_SCType) {
- Error ("Redefinition of '%s' as different kind of symbol", Entry->Name);
- Entry = 0;
+ Error ("Redefinition of '%s' as different kind of symbol", Sym->Name);
+ Sym = 0;
} else if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) {
- Error ("Conflicting types for '%s'", Entry->Name);
- Entry = 0;
+ Error ("Conflicting types for '%s'", Sym->Name);
+ Note ("'%s' vs '%s'", GetFullTypeName (T), GetFullTypeName (E_Type));
+ Sym = 0;
} else if (E_SCType == SC_ENUMERATOR) {
/* Enumerators aren't allowed to be redeclared at all, even if
** all occurences are identical. The current code logic won't
** get here, but let's just do it.
*/
- Error ("Redeclaration of enumerator constant '%s'", Entry->Name);
- Entry = 0;
+ Error ("Redeclaration of enumerator constant '%s'", Sym->Name);
+ Sym = 0;
}
}
}
/* Return if there are any errors */
- return Entry == 0;
+ return Sym == 0;
}
@@ -824,38 +832,38 @@ static void AddSymEntry (SymTable* T, SymEntry* S)
SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab, unsigned* DSFlags)
-/* Add an enum entry and return it */
+/* Add an enum tag entry and return it */
{
SymTable* CurTagTab = TagTab;
- SymEntry* Entry;
+ SymEntry* TagEntry;
if ((Flags & SC_FICTITIOUS) == 0) {
/* Do we have an entry with this name already? */
- Entry = FindSymInTable (CurTagTab, Name, HashStr (Name));
+ TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name));
} else {
/* Add a fictitious symbol in the fail-safe table */
- Entry = 0;
+ TagEntry = 0;
CurTagTab = FailSafeTab;
}
- if (Entry) {
+ if (TagEntry) {
/* We do have an entry. This may be a forward, so check it. */
- if ((Entry->Flags & SC_TYPEMASK) != SC_ENUM) {
+ if ((TagEntry->Flags & SC_TYPEMASK) != SC_ENUM) {
/* Existing symbol is not an enum */
Error ("Symbol '%s' is already different kind", Name);
- Entry = 0;
+ TagEntry = 0;
} else if (Type != 0) {
/* Define the struct size if the underlying type is given. */
- if (Entry->V.E.Type != 0) {
+ if (TagEntry->V.E.Type != 0) {
/* Both are definitions. */
Error ("Multiple definition for 'enum %s'", Name);
- Entry = 0;
+ TagEntry = 0;
} else {
- Entry->V.E.SymTab = Tab;
- Entry->V.E.Type = Type;
- Entry->Flags &= ~SC_DECL;
- Entry->Flags |= SC_DEF;
+ TagEntry->V.E.SymTab = Tab;
+ TagEntry->V.E.Type = Type;
+ TagEntry->Flags &= ~SC_DECL;
+ TagEntry->Flags |= SC_DEF;
/* Remember this is the first definition of this type */
if (DSFlags != 0) {
@@ -864,83 +872,83 @@ SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTab
}
}
- if (Entry == 0) {
+ if (TagEntry == 0) {
/* Use the fail-safe table for fictitious symbols */
CurTagTab = FailSafeTab;
}
}
- if (Entry == 0) {
+ if (TagEntry == 0) {
/* Create a new entry */
- Entry = NewSymEntry (Name, SC_ENUM);
+ TagEntry = NewSymEntry (Name, SC_ENUM);
/* Set the enum type data */
- Entry->V.E.SymTab = Tab;
- Entry->V.E.Type = Type;
+ TagEntry->V.E.SymTab = Tab;
+ TagEntry->V.E.Type = Type;
if (Type != 0) {
- Entry->Flags |= SC_DEF;
+ TagEntry->Flags |= SC_DEF;
}
/* Remember this is the first definition of this type */
if (CurTagTab != FailSafeTab && DSFlags != 0) {
- if ((Entry->Flags & SC_DEF) != 0) {
+ if ((TagEntry->Flags & SC_DEF) != 0) {
*DSFlags |= DS_NEW_TYPE_DEF;
}
*DSFlags |= DS_NEW_TYPE_DECL;
}
/* Add it to the current table */
- AddSymEntry (CurTagTab, Entry);
+ AddSymEntry (CurTagTab, TagEntry);
}
/* Return the entry */
- return Entry;
+ return TagEntry;
}
SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab, unsigned* DSFlags)
-/* Add a struct/union entry and return it */
+/* Add a struct/union tag entry and return it */
{
SymTable* CurTagTab = TagTab;
- SymEntry* Entry;
- unsigned Type = (Flags & SC_TYPEMASK);
+ SymEntry* TagEntry;
+ unsigned SCType = (Flags & SC_TYPEMASK);
- /* Type must be struct or union */
- PRECONDITION (Type == SC_STRUCT || Type == SC_UNION);
+ /* 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? */
- Entry = FindSymInTable (CurTagTab, Name, HashStr (Name));
+ TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name));
} else {
/* Add a fictitious symbol in the fail-safe table */
- Entry = 0;
+ TagEntry = 0;
CurTagTab = FailSafeTab;
}
- if (Entry) {
+ if (TagEntry) {
/* We do have an entry. This may be a forward, so check it. */
- if ((Entry->Flags & SC_TYPEMASK) != Type) {
+ if ((TagEntry->Flags & SC_TYPEMASK) != SCType) {
/* Existing symbol is not a struct */
Error ("Symbol '%s' is already different kind", Name);
- Entry = 0;
- } else if ((Entry->Flags & Flags & SC_DEF) == SC_DEF) {
+ TagEntry = 0;
+ } else if ((TagEntry->Flags & Flags & SC_DEF) == SC_DEF) {
/* Both structs are definitions. */
- if (Type == SC_STRUCT) {
+ if (SCType == SC_STRUCT) {
Error ("Multiple definition for 'struct %s'", Name);
} else {
Error ("Multiple definition for 'union %s'", Name);
}
- Entry = 0;
+ TagEntry = 0;
} else {
/* Define the struct size if it is a definition */
if ((Flags & SC_DEF) == SC_DEF) {
- Entry->Flags = Flags;
- Entry->V.S.SymTab = Tab;
- Entry->V.S.Size = Size;
+ TagEntry->Flags = Flags;
+ TagEntry->V.S.SymTab = Tab;
+ TagEntry->V.S.Size = Size;
/* Remember this is the first definition of this type */
if (DSFlags != 0) {
@@ -949,35 +957,35 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl
}
}
- if (Entry == 0) {
+ if (TagEntry == 0) {
/* Use the fail-safe table for fictitious symbols */
CurTagTab = FailSafeTab;
}
}
- if (Entry == 0) {
+ if (TagEntry == 0) {
/* Create a new entry */
- Entry = NewSymEntry (Name, Flags);
+ TagEntry = NewSymEntry (Name, Flags);
/* Set the struct data */
- Entry->V.S.SymTab = Tab;
- Entry->V.S.Size = Size;
+ TagEntry->V.S.SymTab = Tab;
+ TagEntry->V.S.Size = Size;
/* Remember this is the first definition of this type */
if (CurTagTab != FailSafeTab && DSFlags != 0) {
- if ((Entry->Flags & SC_DEF) != 0) {
+ if ((TagEntry->Flags & SC_DEF) != 0) {
*DSFlags |= DS_NEW_TYPE_DEF;
}
*DSFlags |= DS_NEW_TYPE_DECL;
}
/* Add it to the current tag table */
- AddSymEntry (CurTagTab, Entry);
+ AddSymEntry (CurTagTab, TagEntry);
}
/* Return the entry */
- return Entry;
+ return TagEntry;
}
@@ -1068,7 +1076,7 @@ DefOrRef* AddDefOrRef (SymEntry* E, unsigned Flags)
DOR = xmalloc (sizeof (DefOrRef));
CollAppend (E->V.L.DefsOrRefs, DOR);
- DOR->Line = GetCurrentLine ();
+ DOR->Line = GetCurrentLineNum ();
DOR->LocalsBlockId = (size_t)CollLast (&CurrentFunc->LocalsBlockStack);
DOR->Flags = Flags;
DOR->StackPtr = StackPtr;
@@ -1136,7 +1144,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags)
(size_t)CollAt (AIC, DOR->Depth - 1) != DOR->LocalsBlockId)) {
Warning ("Goto at line %d to label %s jumps into a block with "
"initialization of an object that has automatic storage duration",
- GetCurrentLine (), Name);
+ GetCurrentLineNum (), Name);
}
}
@@ -1279,6 +1287,11 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs
/* Generate the assembler name from the data label number */
Entry->V.L.Label = Offs;
Entry->AsmName = xstrdup (LocalDataLabelName (Entry->V.L.Label));
+ } else if ((Flags & SC_ALIAS) == SC_ALIAS) {
+ /* Just clear the info */
+ Entry->V.A.Field = 0;
+ Entry->V.A.ANumber = 0;
+ Entry->V.A.Offs = 0;
} else {
Internal ("Invalid flags in AddLocalSym: %04X", Flags);
}
@@ -1296,13 +1309,26 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs
SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
/* Add an external or global symbol to the symbol table and return the entry */
{
- /* Start from the local symbol table */
- SymTable* Tab = SymTab;
+ /* Add the new declaration to the global symbol table if no errors */
+ SymTable* Tab = SymTab0;
+
+ /* Only search this name in the local and global symbol tables */
+ SymEntry* Entry = 0;
+ SymEntry* Alias = 0;
+
+ if (SymTab != SymTab0) {
+ Alias = Entry = FindLocalSym (Name);
+ while (Entry && (Entry->Flags & SC_ALIAS) == SC_ALIAS) {
+ /* Get the aliased entry */
+ Entry = Entry->V.A.Field;
+ }
+ }
+
+ if (Entry == 0) {
+ Entry = FindGlobalSym (Name);
+ }
- /* Do we have an entry with this name already? */
- SymEntry* Entry = FindSymInTree (Tab, Name);
if (Entry) {
-
/* We have a symbol with this name already */
if (HandleSymRedefinition (Entry, T, Flags)) {
Entry = 0;
@@ -1317,7 +1343,7 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
** declaration if both declarations are global, otherwise give an
** error.
*/
- if (Tab == SymTab0 &&
+ if (SymTab == SymTab0 &&
(Flags & SC_EXTERN) == 0 &&
(Entry->Flags & SC_EXTERN) != 0) {
Warning ("Static declaration of '%s' follows non-static declaration", Name);
@@ -1353,12 +1379,9 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
/* Use the fail-safe table for fictitious symbols */
Tab = FailSafeTab;
}
-
- } else if ((Flags & (SC_EXTERN | SC_FUNC)) != 0) {
- /* Add the new declaration to the global symbol table instead */
- Tab = SymTab0;
}
- if (Entry == 0 || Entry->Owner != Tab) {
+
+ if (Entry == 0) {
/* Create a new entry */
Entry = NewSymEntry (Name, Flags);
@@ -1376,6 +1399,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
/* Add the entry to the symbol table */
AddSymEntry (Tab, Entry);
+
+ }
+
+ /* Add an alias of the global symbol to the local symbol table */
+ if (Tab == SymTab0 && SymTab != SymTab0 && Entry->Owner != SymTab && Alias == 0) {
+ Alias = AddLocalSym (Name, T, SC_ALIAS, 0);
+ Alias->V.A.Field = Entry;
}
/* Return the entry */
diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h
index 1df61a822..b711fe606 100644
--- a/src/cc65/symtab.h
+++ b/src/cc65/symtab.h
@@ -169,10 +169,10 @@ unsigned short FindSPAdjustment (const char* Name);
SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab, unsigned* DSFlags);
-/* Add an enum entry and return it */
+/* Add an enum tag entry and return it */
SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab, unsigned* DSFlags);
-/* Add a struct/union entry and return it */
+/* Add a struct/union tag entry and return it */
SymEntry* AddBitField (const char* Name, const Type* Type, unsigned Offs,
unsigned BitOffs, unsigned BitWidth, int SignednessSpecified);
diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c
index 6052f4a84..1a108159f 100644
--- a/src/cc65/typecmp.c
+++ b/src/cc65/typecmp.c
@@ -266,18 +266,6 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
LeftType = (GetUnderlyingTypeCode (lhs) & T_MASK_TYPE);
RightType = (GetUnderlyingTypeCode (rhs) & T_MASK_TYPE);
- /* If one side is a pointer and the other side is an array, both are
- ** compatible.
- */
- if (LeftType == T_TYPE_PTR && RightType == T_TYPE_ARRAY) {
- RightType = T_TYPE_PTR;
- SetResult (Result, TC_PTR_DECAY);
- }
- if (LeftType == T_TYPE_ARRAY && RightType == T_TYPE_PTR) {
- LeftType = T_TYPE_PTR;
- SetResult (Result, TC_STRICT_COMPATIBLE);
- }
-
/* Bit-fields are considered compatible if they have the same
** signedness, bit-offset and bit-width.
*/
@@ -287,12 +275,27 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
lhs->A.B.Offs != rhs->A.B.Offs ||
lhs->A.B.Width != rhs->A.B.Width) {
SetResult (Result, TC_INCOMPATIBLE);
+ return;
}
if (LeftType != RightType) {
SetResult (Result, TC_STRICT_COMPATIBLE);
}
}
+ /* If one side is a pointer and the other side is an array, both are
+ ** compatible.
+ */
+ if (Result->Indirections == 0) {
+ if (LeftType == T_TYPE_PTR && RightType == T_TYPE_ARRAY) {
+ RightType = T_TYPE_PTR;
+ SetResult (Result, TC_PTR_DECAY);
+ }
+ if (LeftType == T_TYPE_ARRAY && RightType == T_TYPE_PTR) {
+ LeftType = T_TYPE_PTR;
+ SetResult (Result, TC_STRICT_COMPATIBLE);
+ }
+ }
+
/* If the underlying types are not identical, the types are incompatible */
if (LeftType != RightType) {
SetResult (Result, TC_INCOMPATIBLE);
@@ -303,8 +306,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
if ((IsTypeEnum (lhs) || IsTypeEnum (rhs))) {
/* Compare the tag types */
- Sym1 = IsTypeEnum (lhs) ? GetESUSymEntry (lhs) : 0;
- Sym2 = IsTypeEnum (rhs) ? GetESUSymEntry (rhs) : 0;
+ Sym1 = IsTypeEnum (lhs) ? GetESUTagSym (lhs) : 0;
+ Sym2 = IsTypeEnum (rhs) ? GetESUTagSym (rhs) : 0;
if (Sym1 != Sym2) {
if (Sym1 == 0 || Sym2 == 0) {
@@ -420,8 +423,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
case T_TYPE_STRUCT:
case T_TYPE_UNION:
/* Compare the tag types */
- Sym1 = GetESUSymEntry (lhs);
- Sym2 = GetESUSymEntry (rhs);
+ Sym1 = GetESUTagSym (lhs);
+ Sym2 = GetESUTagSym (rhs);
CHECK (Sym1 != 0 || Sym2 != 0);
diff --git a/src/common/attrib.h b/src/common/attrib.h
index 07e08b2df..3cdacb9d5 100644
--- a/src/common/attrib.h
+++ b/src/common/attrib.h
@@ -44,14 +44,20 @@
-#if defined(__GNUC__)
-# define attribute(a) __attribute__(a)
+#ifdef __clang__
+# define attribute(a) __attribute__(a)
+# define ATTR_UNUSED(x) __attribute__((__unused__)) x
+# define ATTR_NORETURN __attribute__((analyzer_noreturn))
+#elif defined(__GNUC__)
+# define attribute(a) __attribute__(a)
+# define ATTR_UNUSED(x) __attribute__((__unused__)) x
+# define ATTR_NORETURN __attribute__((noreturn))
#else
# define attribute(a)
+# define ATTR_UNUSED(x) x
+# define ATTR_NORETURN
#endif
-
-
/* End of attrib.h */
#endif
diff --git a/src/da65/infofile.c b/src/da65/infofile.c
index 6db82cb36..48a95c9b0 100644
--- a/src/da65/infofile.c
+++ b/src/da65/infofile.c
@@ -592,9 +592,19 @@ static void RangeSection (void)
case INFOTOK_END:
AddAttr ("END", &Attributes, tEnd);
InfoNextTok ();
- InfoAssureInt ();
- InfoRangeCheck (0x0000, 0xFFFF);
- End = InfoIVal;
+
+ if (InfoTok == INFOTOK_OFFSET_INTCON) {
+ InfoRangeCheck (0x0000, 0xFFFF);
+ if (!(Attributes & tStart))
+ InfoError ("When using End with an offset, Start must be specified before");
+ End = Start + InfoIVal - 1;
+ if (End > 0xFFFF)
+ InfoError ("Range error");
+ } else {
+ InfoAssureInt ();
+ InfoRangeCheck (0x0000, 0xFFFF);
+ End = InfoIVal;
+ }
InfoNextTok ();
break;
diff --git a/src/da65/scanner.c b/src/da65/scanner.c
index 33fb3a826..d0301c08a 100644
--- a/src/da65/scanner.c
+++ b/src/da65/scanner.c
@@ -372,6 +372,14 @@ Again:
return;
}
+ /* Decimal number offset? */
+ if (C == '+') {
+ NextChar ();
+ InfoIVal = GetDecimalToken ();
+ InfoTok = INFOTOK_OFFSET_INTCON;
+ return;
+ }
+
/* Other characters */
switch (C) {
diff --git a/src/da65/scanner.h b/src/da65/scanner.h
index d4e38177b..63d3273f6 100644
--- a/src/da65/scanner.h
+++ b/src/da65/scanner.h
@@ -48,6 +48,7 @@
typedef enum token_t {
INFOTOK_NONE,
INFOTOK_INTCON,
+ INFOTOK_OFFSET_INTCON,
INFOTOK_STRCON,
INFOTOK_CHARCON,
INFOTOK_IDENT,
diff --git a/src/sim65/memory.c b/src/sim65/memory.c
index 11f0be55a..68e7bb93b 100644
--- a/src/sim65/memory.c
+++ b/src/sim65/memory.c
@@ -46,7 +46,7 @@
/* THE memory */
-static unsigned char Mem[0x10000];
+unsigned char Mem[0x10000];
@@ -73,14 +73,6 @@ void MemWriteWord (unsigned Addr, unsigned Val)
-unsigned char MemReadByte (unsigned Addr)
-/* Read a byte from a memory location */
-{
- return Mem[Addr];
-}
-
-
-
unsigned MemReadWord (unsigned Addr)
/* Read a word from a memory location */
{
diff --git a/src/sim65/memory.h b/src/sim65/memory.h
index 41cc800d3..cef786aaa 100644
--- a/src/sim65/memory.h
+++ b/src/sim65/memory.h
@@ -36,7 +36,9 @@
#ifndef MEMORY_H
#define MEMORY_H
+#include "inline.h"
+extern unsigned char Mem[0x10000];
/*****************************************************************************/
/* Code */
@@ -50,8 +52,15 @@ void MemWriteByte (unsigned Addr, unsigned char Val);
void MemWriteWord (unsigned Addr, unsigned Val);
/* Write a word to a memory location */
-unsigned char MemReadByte (unsigned Addr);
+#if defined(HAVE_INLINE)
+INLINE unsigned char MemReadByte (unsigned Addr)
/* Read a byte from a memory location */
+{
+ return Mem[Addr];
+}
+#else
+#define MemReadByte(Addr) Mem[Addr]
+#endif
unsigned MemReadWord (unsigned Addr);
/* Read a word from a memory location */
diff --git a/targettest/Makefile b/targettest/Makefile
index 1475d4eb2..a359c7e7a 100644
--- a/targettest/Makefile
+++ b/targettest/Makefile
@@ -761,6 +761,7 @@ TARGETS := \
define TARGET_recipe
+@echo making targettest for: $(T)
@$(MAKE) -j2 SYS:=$(T)
@$(MAKE) --no-print-directory clean SYS:=$(T)
diff --git a/targettest/pce/Makefile b/targettest/pce/Makefile
index f757c3062..89abca6b6 100644
--- a/targettest/pce/Makefile
+++ b/targettest/pce/Makefile
@@ -73,4 +73,7 @@ endif
dd if=$< bs=8K count=${COUNT} >> $@
clean:
- @$(DEL) conio.o conio.??? 2>$(NULLDEV)
+ @$(DEL) conio.o 2>$(NULLDEV)
+ @$(DEL) conio.pce 2>$(NULLDEV)
+ @$(DEL) conio.bin 2>$(NULLDEV)
+ @$(DEL) conio.map 2>$(NULLDEV)
diff --git a/test/asm/listing/Makefile b/test/asm/listing/Makefile
index d3dc21409..3c4c404af 100644
--- a/test/asm/listing/Makefile
+++ b/test/asm/listing/Makefile
@@ -5,25 +5,31 @@ ifneq ($(shell echo),)
endif
ifdef CMD_EXE
+ S = $(subst /,\,/)
EXE = .exe
MKDIR = mkdir $(subst /,\,$1)
RMDIR = -rmdir /q /s $(subst /,\,$1)
+ TRUE = exit 0
+ CAT = type $(subst /,\,$1)
else
+ S = /
EXE =
MKDIR = mkdir -p $1
RMDIR = $(RM) -r $1
+ TRUE = true
+ CAT = cat $1
endif
ifdef QUIET
# .SILENT:
endif
-CA65 := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65)
-LD65 := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65)
+CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65)
+LD65 := $(if $(wildcard ../../../bin/ld65*),..$S..$S..$Sbin$Sld65,ld65)
WORKDIR = ../../../testwrk/asm/listing
-ISEQUAL = ../../../testwrk/isequal$(EXE)
+ISEQUAL = ..$S..$S..$Stestwrk$Sisequal$(EXE)
CC = gcc
CFLAGS = -O2
@@ -50,14 +56,14 @@ $(WORKDIR)/$1.bin: $1.s $(ISEQUAL)
# compile without generating listing
ifeq ($(wildcard control/$1.err),)
- $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2>&1
+ $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2> $$(@:.bin=.err2)
ifeq ($(wildcard control/$1.no-ld65),)
- $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2>&1
+ $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2)
endif
else
- $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2>&1 || true
+ $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2> $$(@:.bin=.err2) || $(TRUE)
ifeq ($(wildcard control/$1.no-ld65),)
- $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2>&1 || true
+ $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) || $(TRUE)
endif
endif
@@ -67,18 +73,25 @@ else
$(ISEQUAL) --empty $$(@:.bin=.err)
endif
+ifneq ($(wildcard ref/$1.err2-ref),)
+ $(ISEQUAL) ref/$1.err2-ref $$(@:.bin=.err2)
+else
+ $(ISEQUAL) --empty $$(@:.bin=.err2)
+endif
+
ifneq ($(wildcard ref/$1.bin-ref),)
$(ISEQUAL) --binary ref/$1.bin-ref $$@
endif
+# rem $(indfo $(CAT) $(subst /,$$S,$$$(@:.bin=.ld65-err)))
+
ifneq ($(wildcard ref/$1.ld65err-ref),)
- @echo cat $$(@:.bin=.ld65-err)
- cat $$(@:.bin=.ld65-err)
- @echo
- @echo
+ @echo $(CAT) $$(@:.bin=.ld65-err)
+# FIXME: somehow this refuses to work in cmd.exe
+ifndef CMD_EXE
+ $(call CAT,$$(@:.bin=.ld65-err))
-diff -u ref/$1.ld65err-ref $$(@:.bin=.ld65-err)
- @echo
- @echo
+endif
$(ISEQUAL) --wildcards ref/$1.ld65err-ref $$(@:.bin=.ld65-err)
else
ifneq ($(wildcard $(WORKDIR)/$1.ld65-err),)
@@ -86,16 +99,30 @@ ifneq ($(wildcard $(WORKDIR)/$1.ld65-err),)
endif
endif
+ifneq ($(wildcard ref/$1.ld65err2-ref),)
+ @echo $(CAT) $$(@:.bin=.ld65-err2)
+# FIXME: somehow this refuses to work in cmd.exe
+ifndef CMD_EXE
+ $(call CAT,$$(@:.bin=.ld65-err2))
+ -diff -u ref/$1.ld65err2-ref $$(@:.bin=.ld65-err2)
+endif
+ $(ISEQUAL) --wildcards ref/$1.ld65err2-ref $$(@:.bin=.ld65-err2)
+else
+ifneq ($(wildcard $(WORKDIR)/$1.ld65-err2),)
+ $(ISEQUAL) --empty $$(@:.bin=.ld65-err2)
+endif
+endif
+
# compile with listing file
ifeq ($(wildcard control/$1.err),)
- $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2>&1
+ $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2> $$(@:.bin=.list-err2)
ifeq ($(wildcard control/$1.no-ld65),)
- $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2>&1
+ $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2)
endif
else
- $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2>&1 || true
+ $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2> $$(@:.bin=.list-err2) || $(TRUE)
ifeq ($(wildcard control/$1.no-ld65),)
- $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2>&1 || true
+ $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) || $(TRUE)
endif
endif
@@ -113,10 +140,26 @@ ifneq ($(wildcard $(WORKDIR)/$1.list-ld65-err),)
endif
endif
+ifneq ($(wildcard ref/$1.err2-ref),)
+ $(ISEQUAL) ref/$1.err2-ref $$(@:.bin=.list-err2)
+else
+ $(ISEQUAL) --empty $$(@:.bin=.list-err2)
+endif
+
+ifneq ($(wildcard ref/$1.ld65err2-ref),)
+ $(ISEQUAL) --wildcards ref/$1.ld65err2-ref $$(@:.bin=.list-ld65-err2)
+else
+ifneq ($(wildcard $(WORKDIR)/$1.list-ld65-err2),)
+ $(ISEQUAL) --empty $$(@:.bin=.list-ld65-err2)
+endif
+endif
+
# check if the result bin is the same as without listing file
ifeq ($(wildcard control/$1.err),)
+ifeq ($(wildcard control/$1.err2),)
$(ISEQUAL) $$@ $$(@:.bin=.list-bin)
endif
+endif
ifneq ($(wildcard ref/$1.list-ref),)
# we have a reference file, compare that, too
diff --git a/test/asm/listing/ref/010-paramcount.err-ref b/test/asm/listing/ref/010-paramcount.err-ref
index a66162eb5..baf73f50f 100644
--- a/test/asm/listing/ref/010-paramcount.err-ref
+++ b/test/asm/listing/ref/010-paramcount.err-ref
@@ -1,15 +1,6 @@
.paramcount = 3
.paramcount = 5
-010-paramcount.s:18: Warning: User warning: r1 is blank!
-010-paramcount.s:14: Note: Macro was defined here
-010-paramcount.s:8: Note: Macro was defined here
.paramcount = 3
.paramcount = 5
-010-paramcount.s:19: Warning: User warning: r1 is blank!
-010-paramcount.s:14: Note: Macro was defined here
-010-paramcount.s:8: Note: Macro was defined here
.paramcount = 1
.paramcount = 5
-010-paramcount.s:20: Warning: User warning: r1 is blank!
-010-paramcount.s:14: Note: Macro was defined here
-010-paramcount.s:8: Note: Macro was defined here
diff --git a/test/asm/listing/ref/010-paramcount.err2-ref b/test/asm/listing/ref/010-paramcount.err2-ref
new file mode 100644
index 000000000..304c9de61
--- /dev/null
+++ b/test/asm/listing/ref/010-paramcount.err2-ref
@@ -0,0 +1,9 @@
+010-paramcount.s:18: Warning: User warning: r1 is blank!
+010-paramcount.s:14: Note: Macro was defined here
+010-paramcount.s:8: Note: Macro was defined here
+010-paramcount.s:19: Warning: User warning: r1 is blank!
+010-paramcount.s:14: Note: Macro was defined here
+010-paramcount.s:8: Note: Macro was defined here
+010-paramcount.s:20: Warning: User warning: r1 is blank!
+010-paramcount.s:14: Note: Macro was defined here
+010-paramcount.s:8: Note: Macro was defined here
diff --git a/test/asm/listing/ref/030-assert-success.err-ref b/test/asm/listing/ref/030-assert-success.err2-ref
similarity index 100%
rename from test/asm/listing/ref/030-assert-success.err-ref
rename to test/asm/listing/ref/030-assert-success.err2-ref
diff --git a/test/asm/listing/ref/030-assert-success.ld65err-ref b/test/asm/listing/ref/030-assert-success.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/030-assert-success.ld65err-ref
rename to test/asm/listing/ref/030-assert-success.ld65err2-ref
diff --git a/test/asm/listing/ref/031-assert-error.err-ref b/test/asm/listing/ref/031-assert-error.err2-ref
similarity index 100%
rename from test/asm/listing/ref/031-assert-error.err-ref
rename to test/asm/listing/ref/031-assert-error.err2-ref
diff --git a/test/asm/listing/ref/032-assert-error2.ld65err-ref b/test/asm/listing/ref/032-assert-error2.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/032-assert-error2.ld65err-ref
rename to test/asm/listing/ref/032-assert-error2.ld65err2-ref
diff --git a/test/asm/listing/ref/032-assert-error3.ld65err-ref b/test/asm/listing/ref/032-assert-error3.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/032-assert-error3.ld65err-ref
rename to test/asm/listing/ref/032-assert-error3.ld65err2-ref
diff --git a/test/asm/listing/ref/032-assert-error4.ld65err-ref b/test/asm/listing/ref/032-assert-error4.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/032-assert-error4.ld65err-ref
rename to test/asm/listing/ref/032-assert-error4.ld65err2-ref
diff --git a/test/asm/listing/ref/032-assert-error5.ld65err-ref b/test/asm/listing/ref/032-assert-error5.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/032-assert-error5.ld65err-ref
rename to test/asm/listing/ref/032-assert-error5.ld65err2-ref
diff --git a/test/asm/listing/ref/032-assert-error6.ld65err-ref b/test/asm/listing/ref/032-assert-error6.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/032-assert-error6.ld65err-ref
rename to test/asm/listing/ref/032-assert-error6.ld65err2-ref
diff --git a/test/asm/listing/ref/032-assert-error7.ld65err-ref b/test/asm/listing/ref/032-assert-error7.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/032-assert-error7.ld65err-ref
rename to test/asm/listing/ref/032-assert-error7.ld65err2-ref
diff --git a/test/asm/listing/ref/032-assert-error8.ld65err-ref b/test/asm/listing/ref/032-assert-error8.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/032-assert-error8.ld65err-ref
rename to test/asm/listing/ref/032-assert-error8.ld65err2-ref
diff --git a/test/asm/listing/ref/033-assert-ldwarning-success.ld65err-ref b/test/asm/listing/ref/033-assert-ldwarning-success.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/033-assert-ldwarning-success.ld65err-ref
rename to test/asm/listing/ref/033-assert-ldwarning-success.ld65err2-ref
diff --git a/test/asm/listing/ref/034-assert-lderror1.err-ref b/test/asm/listing/ref/034-assert-lderror1.err2-ref
similarity index 100%
rename from test/asm/listing/ref/034-assert-lderror1.err-ref
rename to test/asm/listing/ref/034-assert-lderror1.err2-ref
diff --git a/test/asm/listing/ref/034-assert-lderror2.ld65err-ref b/test/asm/listing/ref/034-assert-lderror2.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/034-assert-lderror2.ld65err-ref
rename to test/asm/listing/ref/034-assert-lderror2.ld65err2-ref
diff --git a/test/asm/listing/ref/034-assert-lderror3.ld65err-ref b/test/asm/listing/ref/034-assert-lderror3.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/034-assert-lderror3.ld65err-ref
rename to test/asm/listing/ref/034-assert-lderror3.ld65err2-ref
diff --git a/test/asm/listing/ref/034-assert-lderror4.ld65err-ref b/test/asm/listing/ref/034-assert-lderror4.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/034-assert-lderror4.ld65err-ref
rename to test/asm/listing/ref/034-assert-lderror4.ld65err2-ref
diff --git a/test/asm/listing/ref/034-assert-lderror6.ld65err-ref b/test/asm/listing/ref/034-assert-lderror6.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/034-assert-lderror6.ld65err-ref
rename to test/asm/listing/ref/034-assert-lderror6.ld65err2-ref
diff --git a/test/asm/listing/ref/034-assert-lderror7.ld65err-ref b/test/asm/listing/ref/034-assert-lderror7.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/034-assert-lderror7.ld65err-ref
rename to test/asm/listing/ref/034-assert-lderror7.ld65err2-ref
diff --git a/test/asm/listing/ref/034-assert-lderror8.ld65err-ref b/test/asm/listing/ref/034-assert-lderror8.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/034-assert-lderror8.ld65err-ref
rename to test/asm/listing/ref/034-assert-lderror8.ld65err2-ref
diff --git a/test/asm/listing/ref/040-align.ld65err-ref b/test/asm/listing/ref/040-align.ld65err2-ref
similarity index 100%
rename from test/asm/listing/ref/040-align.ld65err-ref
rename to test/asm/listing/ref/040-align.ld65err2-ref
diff --git a/test/asm/listing/ref/050-case-on-1.err-ref b/test/asm/listing/ref/050-case-on-1.err2-ref
similarity index 100%
rename from test/asm/listing/ref/050-case-on-1.err-ref
rename to test/asm/listing/ref/050-case-on-1.err2-ref
diff --git a/test/asm/listing/ref/050-case-on-2.err-ref b/test/asm/listing/ref/050-case-on-2.err2-ref
similarity index 100%
rename from test/asm/listing/ref/050-case-on-2.err-ref
rename to test/asm/listing/ref/050-case-on-2.err2-ref
diff --git a/test/asm/listing/ref/050-case-on-3.err-ref b/test/asm/listing/ref/050-case-on-3.err2-ref
similarity index 100%
rename from test/asm/listing/ref/050-case-on-3.err-ref
rename to test/asm/listing/ref/050-case-on-3.err2-ref
diff --git a/test/asm/listing/ref/050-case-on-4.err-ref b/test/asm/listing/ref/050-case-on-4.err2-ref
similarity index 100%
rename from test/asm/listing/ref/050-case-on-4.err-ref
rename to test/asm/listing/ref/050-case-on-4.err2-ref
diff --git a/test/asm/listing/ref/050-case-on-5.err-ref b/test/asm/listing/ref/050-case-on-5.err2-ref
similarity index 100%
rename from test/asm/listing/ref/050-case-on-5.err-ref
rename to test/asm/listing/ref/050-case-on-5.err2-ref
diff --git a/test/err/bug1890.c b/test/err/bug1890.c
new file mode 100644
index 000000000..15d857cdb
--- /dev/null
+++ b/test/err/bug1890.c
@@ -0,0 +1,9 @@
+/* bug #1890 - Overflow in enumerator value is not detected */
+
+#include
+enum { a = ULONG_MAX, b } c = b;
+
+int main(void)
+{
+ return 0;
+}
diff --git a/test/err/bug1893.c b/test/err/bug1893.c
new file mode 100644
index 000000000..455256179
--- /dev/null
+++ b/test/err/bug1893.c
@@ -0,0 +1,8 @@
+/* bug #1893 - Compiler accepts a ternary expression where it shouldn't */
+
+int main(void)
+{
+ int a, b, c;
+ a == 1? b : c = 3;
+ return 0;
+}
diff --git a/test/err/bug1895-assign1a.c b/test/err/bug1895-assign1a.c
new file mode 100644
index 000000000..223964104
--- /dev/null
+++ b/test/err/bug1895-assign1a.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_1_SUB_1_A
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-assign1b.c b/test/err/bug1895-assign1b.c
new file mode 100644
index 000000000..cccc0a318
--- /dev/null
+++ b/test/err/bug1895-assign1b.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_1_SUB_1_B
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-assign2a.c b/test/err/bug1895-assign2a.c
new file mode 100644
index 000000000..512b658a9
--- /dev/null
+++ b/test/err/bug1895-assign2a.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_1_SUB_2_A
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-assign2b.c b/test/err/bug1895-assign2b.c
new file mode 100644
index 000000000..d07191206
--- /dev/null
+++ b/test/err/bug1895-assign2b.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_1_SUB_2_B
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-assign4a.c b/test/err/bug1895-assign4a.c
new file mode 100644
index 000000000..c2a6f25de
--- /dev/null
+++ b/test/err/bug1895-assign4a.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_1_SUB_4_A
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-assign4b.c b/test/err/bug1895-assign4b.c
new file mode 100644
index 000000000..740e10b04
--- /dev/null
+++ b/test/err/bug1895-assign4b.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_1_SUB_4_B
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-assign5a.c b/test/err/bug1895-assign5a.c
new file mode 100644
index 000000000..fed4e07d9
--- /dev/null
+++ b/test/err/bug1895-assign5a.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_1_SUB_5_A
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-assign5b.c b/test/err/bug1895-assign5b.c
new file mode 100644
index 000000000..ed8498e34
--- /dev/null
+++ b/test/err/bug1895-assign5b.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_1_SUB_5_B
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-common.h b/test/err/bug1895-common.h
new file mode 100644
index 000000000..03f02e2de
--- /dev/null
+++ b/test/err/bug1895-common.h
@@ -0,0 +1,196 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types
+
+ Test of incompatible pointer/array types in assignment ans conditional
+ expressions, as well as function prototypes.
+
+ In each source file, define a single macro and include this file to perform
+ a coresponding test individually.
+
+ https://github.com/cc65/cc65/issues/1895
+*/
+
+/* Test 1 suite */
+#ifdef DO_TEST_1_SUB_1_A
+#define TEST_1_SUB_1_A CMP_TYPES_1
+#else
+#define TEST_1_SUB_1_A BLANK
+#endif
+
+#ifdef DO_TEST_1_SUB_1_B
+#define TEST_1_SUB_1_B CMP_TYPES_1
+#else
+#define TEST_1_SUB_1_B BLANK
+#endif
+
+#ifdef DO_TEST_1_SUB_2_A
+#define TEST_1_SUB_2_A CMP_TYPES_1
+#else
+#define TEST_1_SUB_2_A BLANK
+#endif
+
+#ifdef DO_TEST_1_SUB_2_B
+#define TEST_1_SUB_2_B CMP_TYPES_1
+#else
+#define TEST_1_SUB_2_B BLANK
+#endif
+
+#ifdef DO_TEST_1_SUB_4_A
+#define TEST_1_SUB_4_A CMP_TYPES_1
+#else
+#define TEST_1_SUB_4_A BLANK
+#endif
+
+#ifdef DO_TEST_1_SUB_4_B
+#define TEST_1_SUB_4_B CMP_TYPES_1
+#else
+#define TEST_1_SUB_4_B BLANK
+#endif
+
+#ifdef DO_TEST_1_SUB_5_A
+#define TEST_1_SUB_5_A CMP_TYPES_1
+#else
+#define TEST_1_SUB_5_A BLANK
+#endif
+
+#ifdef DO_TEST_1_SUB_5_B
+#define TEST_1_SUB_5_B CMP_TYPES_1
+#else
+#define TEST_1_SUB_5_B BLANK
+#endif
+
+/* Test 2 suite */
+#ifdef DO_TEST_2_SUB_1
+#define TEST_2_SUB_1 CMP_TYPES_2
+#else
+#define TEST_2_SUB_1 BLANK
+#endif
+
+#ifdef DO_TEST_2_SUB_2
+#define TEST_2_SUB_2 CMP_TYPES_2
+#else
+#define TEST_2_SUB_2 BLANK
+#endif
+
+#ifdef DO_TEST_2_SUB_3
+#define TEST_2_SUB_3 CMP_TYPES_2
+#else
+#define TEST_2_SUB_3 BLANK
+#endif
+
+#ifdef DO_TEST_2_SUB_4
+#define TEST_2_SUB_4 CMP_TYPES_2
+#else
+#define TEST_2_SUB_4 BLANK
+#endif
+
+#ifdef DO_TEST_2_SUB_5
+#define TEST_2_SUB_5 CMP_TYPES_2
+#else
+#define TEST_2_SUB_5 BLANK
+#endif
+
+/* Test 3 suite */
+#ifdef DO_TEST_3_SUB_1
+#define TEST_3_SUB_1 CMP_TYPES_3
+#else
+#define TEST_3_SUB_1 BLANK
+#endif
+
+#ifdef DO_TEST_3_SUB_2
+#define TEST_3_SUB_2 CMP_TYPES_3
+#else
+#define TEST_3_SUB_2 BLANK
+#endif
+
+#ifdef DO_TEST_3_SUB_3
+#define TEST_3_SUB_3 CMP_TYPES_3
+#else
+#define TEST_3_SUB_3 BLANK
+#endif
+
+#ifdef DO_TEST_3_SUB_4
+#define TEST_3_SUB_4 CMP_TYPES_3
+#else
+#define TEST_3_SUB_4 BLANK
+#endif
+
+#ifdef DO_TEST_3_SUB_5
+#define TEST_3_SUB_5 CMP_TYPES_3
+#else
+#define TEST_3_SUB_5 BLANK
+#endif
+
+/* Implementation */
+#define CONCAT(a, b) CONCAT_impl_(a, b)
+#define CONCAT_impl_(a, b) a##b
+#define BLANK(...)
+#define DECL_FUNCS(A, B)\
+ void CONCAT(foo_,__LINE__)(A); void CONCAT(foo_,__LINE__)(B);
+
+/* Test with assignment */
+#define CMP_TYPES_1(A, B)\
+ do {\
+ A p; B q;\
+_Pragma("warn(error, on)")\
+ p = q;\
+_Pragma("warn(error, off)")\
+ } while (0)
+
+/* Test with conditional expression */
+#define CMP_TYPES_2(A, B)\
+ do {\
+ A p; B q;\
+_Pragma("warn(error, on)")\
+ v = v ? p : q;\
+_Pragma("warn(error, off)")\
+ } while (0)
+
+/* Test with function prototype */
+#define CMP_TYPES_3(A, B)\
+ do {\
+ DECL_FUNCS(A,B);\
+ } while (0)
+
+static void *v;
+
+typedef int (*p1)[3]; /* pointer to array */
+typedef int **q1; /* pointer to pointer */
+typedef int (**p2)[3]; /* pointer to pointer to array */
+typedef int ***q2; /* pointer to pointer to pointer */
+typedef int p3[1][3]; /* array of array */
+typedef int *q3[1]; /* array of pointer */
+typedef int const **p4; /* pointer to pointer to const */
+typedef int **q4; /* pointer to pointer to non-const */
+typedef int (*p5)(int (*)(p3)); /* pointer to function taking pointer to function taking pointer to array */
+typedef int (*q5)(int (*)(q3)); /* pointer to function taking pointer to function taking pointer to pointer */
+
+int main(void)
+{
+ /* Warnings */
+ TEST_1_SUB_1_A(p1, q1);
+ TEST_1_SUB_1_B(q1, p1);
+ TEST_1_SUB_2_A(p2, q2);
+ TEST_1_SUB_2_B(q2, p2);
+ /* TEST_1_SUB_3_A(p3, q3); */
+ /* TEST_1_SUB_3_B(q3, p3); */
+ TEST_1_SUB_4_A(p4, q4);
+ TEST_1_SUB_4_B(q4, p4);
+ TEST_1_SUB_5_A(p5, q5);
+ TEST_1_SUB_5_B(q5, p5);
+
+ /* GCC and clang give warnings while cc65 gives errors */
+ TEST_2_SUB_1(p1, q1);
+ TEST_2_SUB_2(p2, q2);
+ TEST_2_SUB_3(p3, q3);
+ TEST_2_SUB_4(p4, q4);
+ TEST_2_SUB_5(p5, q5);
+
+ /* Errors */
+ TEST_3_SUB_1(p1, q1);
+ TEST_3_SUB_2(p2, q2);
+ TEST_3_SUB_3(p3, q3);
+ TEST_3_SUB_4(p4, q4);
+ TEST_3_SUB_5(p5, q5);
+
+ return 0;
+}
diff --git a/test/err/bug1895-cond1.c b/test/err/bug1895-cond1.c
new file mode 100644
index 000000000..ac6a301c9
--- /dev/null
+++ b/test/err/bug1895-cond1.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_2_SUB_1
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-cond2.c b/test/err/bug1895-cond2.c
new file mode 100644
index 000000000..81b8b2912
--- /dev/null
+++ b/test/err/bug1895-cond2.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_2_SUB_2
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-cond3.c b/test/err/bug1895-cond3.c
new file mode 100644
index 000000000..f1571acbf
--- /dev/null
+++ b/test/err/bug1895-cond3.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_2_SUB_3
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-cond4.c b/test/err/bug1895-cond4.c
new file mode 100644
index 000000000..e7ef77964
--- /dev/null
+++ b/test/err/bug1895-cond4.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_2_SUB_4
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-cond5.c b/test/err/bug1895-cond5.c
new file mode 100644
index 000000000..1625c599b
--- /dev/null
+++ b/test/err/bug1895-cond5.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_2_SUB_5
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-prototype1.c b/test/err/bug1895-prototype1.c
new file mode 100644
index 000000000..30331e757
--- /dev/null
+++ b/test/err/bug1895-prototype1.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_3_SUB_1
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-prototype2.c b/test/err/bug1895-prototype2.c
new file mode 100644
index 000000000..c8fe213f4
--- /dev/null
+++ b/test/err/bug1895-prototype2.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_3_SUB_2
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-prototype3.c b/test/err/bug1895-prototype3.c
new file mode 100644
index 000000000..f7d2b79e4
--- /dev/null
+++ b/test/err/bug1895-prototype3.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_3_SUB_3
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-prototype4.c b/test/err/bug1895-prototype4.c
new file mode 100644
index 000000000..3d0fe2c05
--- /dev/null
+++ b/test/err/bug1895-prototype4.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_3_SUB_4
+
+#include "bug1895-common.h"
diff --git a/test/err/bug1895-prototype5.c b/test/err/bug1895-prototype5.c
new file mode 100644
index 000000000..ba9b997c9
--- /dev/null
+++ b/test/err/bug1895-prototype5.c
@@ -0,0 +1,5 @@
+/* Bug #1895 - missing diagnostics on incompatible pointer/array types */
+
+#define DO_TEST_3_SUB_5
+
+#include "bug1895-common.h"
diff --git a/test/misc/Makefile b/test/misc/Makefile
index e77d37b29..d0b8979b0 100644
--- a/test/misc/Makefile
+++ b/test/misc/Makefile
@@ -59,9 +59,9 @@ $(ISEQUAL): ../isequal.c | $(WORKDIR)
define PRG_template
# should compile, but gives an error
-$(WORKDIR)/bug1768.$1.$2.prg: bug1768.c | $(WORKDIR)
+$(WORKDIR)/int-static-1888.$1.$2.prg: int-static-1888.c | $(WORKDIR)
@echo "FIXME: " $$@ "currently does not compile."
- $(if $(QUIET),echo misc/bug1768.$1.$2.prg)
+ $(if $(QUIET),echo misc/int-static-1888.$1.$2.prg)
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
# should compile, but gives an error
@@ -132,6 +132,14 @@ $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR)
$(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR)
$(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR)
+# this one requires -Werror
+$(WORKDIR)/bug1768.$1.$2.prg: bug1768.c | $(WORKDIR)
+ $(if $(QUIET),echo misc/bug1768.$1.$2.prg)
+ $(CC65) -Werror -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR)
+ $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR)
+ $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR)
+ $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR)
+
# should compile, but then hangs in an endless loop
$(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR)
$(if $(QUIET),echo misc/endless.$1.$2.prg)
diff --git a/test/misc/bug1768.c b/test/misc/bug1768.c
index 916aa64bc..35cee1049 100644
--- a/test/misc/bug1768.c
+++ b/test/misc/bug1768.c
@@ -1,14 +1,147 @@
+/*
+ Copyright 2021-2022, The cc65 Authors
-#include
+ 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.
-int a = 1 || (8 / 0);
-int b = 0 && (8 % 0);
-int c = 1 ? 42 : (0 % 0);
-int d = 1 || a / 0;
-int e = 0 && b % 0;
-int f = 1 ? 42 : (a %= 0, b /= 0);
+ 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 operations in unevaluated context resulted from 'sizeof' and
+ short-circuited code-paths in AND, OR and conditional operations.
+
+ See also:
+ https://github.com/cc65/cc65/issues/1768#issuecomment-1175221466
+*/
+
+#include
+
+static int failures;
+
+#define TEST(EXPR)\
+ {\
+ int acc = 0;\
+ acc += sizeof((EXPR), 0);\
+ acc += (0 && (EXPR));\
+ acc += (1 || (EXPR));\
+ acc += (0 ? (EXPR) : 0);\
+ acc += (1 ? 0 : (EXPR));\
+ if (acc == 0) {\
+ printf("acc = %d\n", acc);\
+ ++failures;\
+ }\
+ }
+
+/* Division by zero/modulo with zero */
+void test_1(void)
+{
+ int i;
+ int j;
+ TEST((i / 0) | (j % 0))
+}
+
+/* Division by zero/modulo with zero */
+void test_2(void)
+{
+ int i;
+ int j;
+ TEST((i /= 0) | (j %= 0))
+}
+
+/* Shift by too wide counts */
+void test_3(void)
+{
+ int i;
+ int j;
+ TEST((i << 32) | (j >> 32))
+}
+
+/* Shift by too wide counts */
+void test_4(void)
+{
+ int i;
+ int j;
+ TEST((i <<= 32) | (j >>= 32))
+}
+
+/* Shift by negative counts */
+void test_5(void)
+{
+ int i;
+ int j;
+ TEST((i << -1) | (j >> -1))
+}
+
+/* Shift by negative counts */
+void test_6(void)
+{
+ int i;
+ int j;
+ TEST((i <<= -1) | (j >>= -1))
+}
+
+/* Shift bit-fields */
+void test_7(void)
+{
+ struct S {
+ long i : 24; /* Will be promoted to 32-bit integer in calculation */
+ long j : 8; /* Will be promoted to 16-bit integer in calculation */
+ } s;
+ long k;
+
+ s.i = 1;
+ printf("%u\n", sizeof(s.i << 24));
+ s.i = 2;
+ k = s.i << 16;
+ if (k != 0x00020000L) {
+ printf("k = %ld, expected: %ld\n", k, 0x00020000L);
+ }
+ TEST(s.j >> 16)
+}
+
+/* Shift bit-fields */
+void test_8(void)
+{
+ struct S {
+ long i : 24; /* Will be promoted to 32-bit integer in calculation */
+ long j : 8; /* Will be promoted to 16-bit integer in calculation */
+ } s;
+ long k;
+
+ s.i = 3;
+ printf("%u\n", sizeof(s.i << 24));
+ s.i = 4;
+ k = s.i <<= 16;
+ if (k != 0x00040000L) {
+ printf("k = %ld, expected: %ld\n", k, 0x00040000L);
+ }
+ TEST(s.j >>= 8)
+}
+
+/* Do all tests */
int main(void)
{
- return EXIT_SUCCESS;
+ test_1();
+ test_2();
+ test_3();
+ test_4();
+ test_5();
+ test_6();
+ test_7();
+ test_8();
+
+ printf("Failures: %d\n", failures);
+ return failures;
}
diff --git a/test/readme.txt b/test/readme.txt
index dd87ea9df..41d19aee3 100644
--- a/test/readme.txt
+++ b/test/readme.txt
@@ -20,6 +20,28 @@ compiler is working as expected (when the tests behave as described):
library.
/ref - These tests produce output that must be compared with reference output.
+ Normally the reference output is produced by compiling the program on the
+ host (using gcc mostly) and then running them on the host. Tests should
+ be tweaked to produce the same output as on the host in the cases where
+ it would be different.
+
+ The Makefile also handles some special cases (add the tests to the
+ respective list in the makefile):
+
+ - Sometimes we want to check the warnings produced by the compiler. In
+ that case use the CUSTOMSOURCES list. Whatever output the compiler writes
+ to stderr will be compared against the matching .cref file. There is an
+ example in custom-reference.c/.cref
+
+ - Sometimes we want to check what kind of output the compiler produces
+ for a file that does not compile. In that case use the ERRORSOURCES list.
+ There is an example in custom-reference-error.c/.cref
+
+ Warning: please understand that comparing the compiler output against
+ a reference produces a moving target, ie the tests may break randomly
+ at any time when the compiler output changes for whatever reason. So
+ only ever use this as a last resort when something can not be tested by
+ other means.
/err - contains tests that MUST NOT compile
diff --git a/test/ref/Makefile b/test/ref/Makefile
index d9c9817ee..abd3e9bc0 100644
--- a/test/ref/Makefile
+++ b/test/ref/Makefile
@@ -11,12 +11,14 @@ ifdef CMD_EXE
NULLDEV = nul:
MKDIR = mkdir $(subst /,\,$1)
RMDIR = -rmdir /s /q $(subst /,\,$1)
+ COPY = copy $(subst /,\,$1) $(subst /,\,$2)
else
S = /
EXE =
NULLDEV = /dev/null
MKDIR = mkdir -p $1
RMDIR = $(RM) -r $1
+ COPY = cp $1 $2
endif
ifdef QUIET
@@ -42,24 +44,48 @@ CFLAGS = -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow
.PHONY: all clean
-SOURCES := $(wildcard *.c)
+# list of sources that produces warnings that we want to check. a .cref file
+# containing the exact output is required.
+CUSTOMSOURCES = \
+ custom-reference.c
+
+# list of sources that produce a compiler error. a .cref files containing the
+# exact error output is required
+ERRORSOURCES = \
+ custom-reference-error.c \
+ bug1889-missing-identifier.c
+
+SOURCES := $(filter-out $(CUSTOMSOURCES) $(ERRORSOURCES),$(wildcard *.c))
+
REFS = $(SOURCES:%.c=$(WORKDIR)/%.ref)
+CUSTOMREFS = $(CUSTOMSOURCES:%.c=$(WORKDIR)/%.cref) $(ERRORSOURCES:%.c=$(WORKDIR)/%.cref)
+
TESTS = $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).6502.prg))
TESTS += $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).65c02.prg))
-all: $(REFS) $(TESTS)
+CUSTOMTESTS = $(foreach option,$(OPTIONS),$(CUSTOMSOURCES:%.c=$(WORKDIR)/%.$(option).6502.custom.prg))
+CUSTOMTESTS += $(foreach option,$(OPTIONS),$(CUSTOMSOURCES:%.c=$(WORKDIR)/%.$(option).65c02.custom.prg))
+
+ERRORTESTS = $(foreach option,$(OPTIONS),$(ERRORSOURCES:%.c=$(WORKDIR)/%.$(option).6502.error.prg))
+ERRORTESTS += $(foreach option,$(OPTIONS),$(ERRORSOURCES:%.c=$(WORKDIR)/%.$(option).65c02.error.prg))
+
+all: $(CUSTOMREFS) $(REFS) $(TESTS) $(CUSTOMTESTS) $(ERRORTESTS)
$(WORKDIR):
$(call MKDIR,$(WORKDIR))
+$(ISEQUAL): ../isequal.c | $(WORKDIR)
+ $(CC) $(CFLAGS) -o $@ $<
+
+$(WORKDIR)/%.cref: %.cref | $(WORKDIR)
+ $(if $(QUIET),echo ref/$*.cref)
+ $(call COPY,$*.cref,$@)
+
$(WORKDIR)/%.ref: %.c | $(WORKDIR)
$(if $(QUIET),echo ref/$*.host)
$(CC) $(CFLAGS) -o $(WORKDIR)/$*.host $< $(NULLERR)
$(WORKDIR)$S$*.host > $@
-$(ISEQUAL): ../isequal.c | $(WORKDIR)
- $(CC) $(CFLAGS) -o $@ $<
-
# "yaccdbg.c" includes "yacc.c".
# yaccdbg's built files must depend on both of them.
#
@@ -78,8 +104,43 @@ $(WORKDIR)/%.$1.$2.prg: %.c $(WORKDIR)/%.ref $(ISEQUAL)
endef # PRG_template
+# extra template for the case when compilation works, but we still want to
+# compare the warning output with our custom reference
+define PRG_custom_template
+
+$(WORKDIR)/%.$1.$2.custom.prg: %.c $(WORKDIR)/%.ref %.c $(WORKDIR)/%.cref $(ISEQUAL)
+ $(if $(QUIET),echo cref/$$*.$1.$2.custom.prg)
+ -$(CC65) -t sim$2 $$(CC65FLAGS) -$1 -o $$(@:.custom.prg=.s) $$< 2> $(WORKDIR)/$$*.$1.$2.cout
+ $(CA65) -t sim$2 -o $$(@:.custom.prg=.o) $$(@:.custom.prg=.s) $(NULLERR)
+ $(LD65) -t sim$2 -o $$@ $$(@:.custom.prg=.o) sim$2.lib $(NULLERR)
+ $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/$$*.$1.$2.out
+ $(ISEQUAL) $(WORKDIR)/$$*.$1.$2.cout $(WORKDIR)/$$*.cref
+ $(ISEQUAL) $(WORKDIR)/$$*.$1.$2.out $(WORKDIR)/$$*.ref
+
+endef # PRG_error_template
+
+# extra template for the case when compilation fails, but we still want to
+# compare the error output with our custom reference
+define PRG_error_template
+
+$(WORKDIR)/%.$1.$2.error.prg: %.c $(WORKDIR)/%.cref $(ISEQUAL)
+ $(if $(QUIET),echo cref/$$*.$1.$2.error.prg)
+ -$(CC65) -t sim$2 $$(CC65FLAGS) -$1 -o $$(@:.error.prg=.s) $$< 2> $(WORKDIR)/$$*.$1.$2.cout
+# $(CA65) -t sim$2 -o $$(@:.error.prg=.o) $$(@:.error.prg=.s) $(NULLERR)
+# $(LD65) -t sim$2 -o $$@ $$(@:.error.prg=.o) sim$2.lib $(NULLERR)
+# $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/$$*.$1.$2.out
+ $(ISEQUAL) $(WORKDIR)/$$*.$1.$2.cout $(WORKDIR)/$$*.cref
+
+endef # PRG_error_template
+
$(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),6502)))
$(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02)))
+$(foreach option,$(OPTIONS),$(eval $(call PRG_custom_template,$(option),6502)))
+$(foreach option,$(OPTIONS),$(eval $(call PRG_custom_template,$(option),65c02)))
+
+$(foreach option,$(OPTIONS),$(eval $(call PRG_error_template,$(option),6502)))
+$(foreach option,$(OPTIONS),$(eval $(call PRG_error_template,$(option),65c02)))
+
clean:
@$(call RMDIR,$(WORKDIR))
diff --git a/test/ref/bug1889-missing-identifier.c b/test/ref/bug1889-missing-identifier.c
new file mode 100644
index 000000000..d9cf4aa52
--- /dev/null
+++ b/test/ref/bug1889-missing-identifier.c
@@ -0,0 +1,9 @@
+/* bug 1889 - endless errors due to failure in recovery from missing identifier */
+
+int enum { a } x;
+inline enum { b };
+
+int main(void)
+{
+ return 0;
+}
diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref
new file mode 100644
index 000000000..cd3f76849
--- /dev/null
+++ b/test/ref/bug1889-missing-identifier.cref
@@ -0,0 +1,3 @@
+bug1889-missing-identifier.c:3: Error: Identifier expected
+bug1889-missing-identifier.c:4: Error: Identifier expected
+bug1889-missing-identifier.c:4: Warning: Implicit 'int' is an obsolete feature
diff --git a/test/ref/custom-reference-error.c b/test/ref/custom-reference-error.c
new file mode 100644
index 000000000..c86a8b9e9
--- /dev/null
+++ b/test/ref/custom-reference-error.c
@@ -0,0 +1,21 @@
+
+/*
+ this is an example (not actually a regression test) that shows how to
+ make a check that compares the compiler (error-) output with a provided
+ reference.
+
+ to produce a reference file, first make sure your program "works" as intended,
+ then "make" in this directory once and copy the produced compiler output to
+ the reference:
+
+ $ cp ../../testwrk/ref/custom-reference-error.g.6502.out custom-reference-error.cref
+
+ and then "make" again to confirm
+*/
+
+int main(int argc, char* argv[])
+{
+ printf("%02x", 0x42);
+ n = 0; /* produce an error */
+ /* another error */
+}
diff --git a/test/ref/custom-reference-error.cref b/test/ref/custom-reference-error.cref
new file mode 100644
index 000000000..fa584f307
--- /dev/null
+++ b/test/ref/custom-reference-error.cref
@@ -0,0 +1,5 @@
+custom-reference-error.c:18: Error: Call to undeclared function 'printf'
+custom-reference-error.c:19: Error: Undefined symbol: 'n'
+custom-reference-error.c:21: Warning: Control reaches end of non-void function [-Wreturn-type]
+custom-reference-error.c:21: Warning: Parameter 'argc' is never used
+custom-reference-error.c:21: Warning: Parameter 'argv' is never used
diff --git a/test/ref/custom-reference.c b/test/ref/custom-reference.c
new file mode 100644
index 000000000..5d9c356df
--- /dev/null
+++ b/test/ref/custom-reference.c
@@ -0,0 +1,24 @@
+
+/*
+ this is an example (not actually a regression test) that shows how to
+ make a check that compares the compiler (error-) output with a provided
+ reference.
+
+ to produce a reference file, first make sure your program "works" as intended,
+ then "make" in this directory once and copy the produced compiler output to
+ the reference:
+
+ $ cp ../../testwrk/ref/custom-reference.g.6502.out custom-reference.cref
+
+ and then "make" again to confirm
+*/
+
+#include
+#include
+
+int main(int argc, char* argv[])
+{
+ printf("%02x", 0x42);
+ /* produce a warning */
+ return 0;
+}
diff --git a/test/ref/custom-reference.cref b/test/ref/custom-reference.cref
new file mode 100644
index 000000000..4dba6009b
--- /dev/null
+++ b/test/ref/custom-reference.cref
@@ -0,0 +1,2 @@
+custom-reference.c:24: Warning: Parameter 'argc' is never used
+custom-reference.c:24: Warning: Parameter 'argv' is never used
diff --git a/test/todo/sprintf-test.c b/test/todo/sprintf-test.c
index bd5de44b4..ea50f418a 100644
--- a/test/todo/sprintf-test.c
+++ b/test/todo/sprintf-test.c
@@ -564,9 +564,9 @@ int main (void)
/* Output the result */
if (Failures) {
- printf ("%u tests, %u failures\n", Tests, Failures);
+ printf ("sprintf-test: %u tests, %u failures\n", Tests, Failures);
} else {
- printf ("%u tests: Ok\n", Tests);
+ printf ("sprintf-test: %u tests: Ok\n", Tests);
}
/* Wait for a key so we can read the result */
diff --git a/test/val/assign-use1.c b/test/val/assign-shift-use1.c
similarity index 100%
rename from test/val/assign-use1.c
rename to test/val/assign-shift-use1.c
diff --git a/test/val/fields.c b/test/val/bitfield-1.c
similarity index 100%
rename from test/val/fields.c
rename to test/val/bitfield-1.c
diff --git a/test/val/char-bitfield.c b/test/val/bitfield-char-1.c
similarity index 100%
rename from test/val/char-bitfield.c
rename to test/val/bitfield-char-1.c
diff --git a/test/val/call1.c b/test/val/boolean-call1.c
similarity index 100%
rename from test/val/call1.c
rename to test/val/boolean-call1.c
diff --git a/test/val/bug1047.c b/test/val/bug1047-bitfield-char.c
similarity index 100%
rename from test/val/bug1047.c
rename to test/val/bug1047-bitfield-char.c
diff --git a/test/val/bug1094.c b/test/val/bug1094-nested-init.c
similarity index 100%
rename from test/val/bug1094.c
rename to test/val/bug1094-nested-init.c
diff --git a/test/val/bug1095.c b/test/val/bug1095-bitfield-signed.c
similarity index 100%
rename from test/val/bug1095.c
rename to test/val/bug1095-bitfield-signed.c
diff --git a/test/val/bug1139.c b/test/val/bug1139-bitfield-in-if.c
similarity index 100%
rename from test/val/bug1139.c
rename to test/val/bug1139-bitfield-in-if.c
diff --git a/test/val/bug1178.c b/test/val/bug1178-struct-copy.c
similarity index 100%
rename from test/val/bug1178.c
rename to test/val/bug1178-struct-copy.c
diff --git a/test/val/bug1181.c b/test/val/bug1181-struct-field-null-cmp.c
similarity index 100%
rename from test/val/bug1181.c
rename to test/val/bug1181-struct-field-null-cmp.c
diff --git a/test/val/bug1267.c b/test/val/bug1267-bitfield-typedef-signedness.c
similarity index 100%
rename from test/val/bug1267.c
rename to test/val/bug1267-bitfield-typedef-signedness.c
diff --git a/test/val/bug1332.c b/test/val/bug1332-bitfield.c
similarity index 100%
rename from test/val/bug1332.c
rename to test/val/bug1332-bitfield.c
diff --git a/test/val/bug1357.c b/test/val/bug1357-pp.c
similarity index 100%
rename from test/val/bug1357.c
rename to test/val/bug1357-pp.c
diff --git a/test/val/bug1408.c b/test/val/bug1408.c
index 8ecc1be68..137899315 100644
--- a/test/val/bug1408.c
+++ b/test/val/bug1408.c
@@ -1,41 +1,41 @@
-/* Bug #1408: Signed char type comparisons with unsigned numeric constants */
-
-#include
-
-static int failures = 0;
-static signed char x = -1;
-
-int main(void)
-{
- if (!(x > -2u)) {
- printf("x > -2u should be true\n");
- ++failures;
- }
- if (!(x > 0u)) {
- printf("x > 0u should be true\n");
- ++failures;
- }
- if (!(x > 255u)) {
- printf("x > 255u should be true\n");
- ++failures;
- }
-
- if (!(-2u < x)) {
- printf("-2u < x should be true\n");
- ++failures;
- }
- if (!(0u < x)) {
- printf("0u < x should be true\n");
- ++failures;
- }
- if (!(255u < x)) {
- printf("255u < x should be true\n");
- ++failures;
- }
-
- if (failures != 0) {
- printf("Failures: %d\n", failures);
- }
-
- return failures;
-}
+/* Bug #1408: Signed char type comparisons with unsigned numeric constants */
+
+#include
+
+static int failures = 0;
+static signed char x = -1;
+
+int main(void)
+{
+ if (!(x > -2u)) {
+ printf("x > -2u should be true\n");
+ ++failures;
+ }
+ if (!(x > 0u)) {
+ printf("x > 0u should be true\n");
+ ++failures;
+ }
+ if (!(x > 255u)) {
+ printf("x > 255u should be true\n");
+ ++failures;
+ }
+
+ if (!(-2u < x)) {
+ printf("-2u < x should be true\n");
+ ++failures;
+ }
+ if (!(0u < x)) {
+ printf("0u < x should be true\n");
+ ++failures;
+ }
+ if (!(255u < x)) {
+ printf("255u < x should be true\n");
+ ++failures;
+ }
+
+ if (failures != 0) {
+ printf("Failures: %d\n", failures);
+ }
+
+ return failures;
+}
diff --git a/test/val/bug1451.c b/test/val/bug1451-struct-ptr-to-local.c
similarity index 94%
rename from test/val/bug1451.c
rename to test/val/bug1451-struct-ptr-to-local.c
index f9cca2561..2148e034d 100644
--- a/test/val/bug1451.c
+++ b/test/val/bug1451-struct-ptr-to-local.c
@@ -1,39 +1,39 @@
-/* Bug #1451 - local struct field access via the address of the struct */
-
-#include
-
-typedef struct {
- int a;
- int b;
-} S;
-
-int failures = 0;
-
-int main(void)
-{
- S a = {2, 5};
- S b = {1, 4};
- S m[1] = {{6, 3}};
- S *p = &a;
-
- (&a)->a += b.a;
- p->b += b.b;
- m->a += b.a;
-
- if ((&a)->a != 3) {
- ++failures;
- printf("Expected 3, got %d\n", (&a)->a);
- }
-
- if (p->b != 9) {
- ++failures;
- printf("Expected 9, got %d\n", p->b);
- }
-
- if (m->a != 7) {
- ++failures;
- printf("Expected 7, got %d\n", m->a);
- }
-
- return failures;
-}
+/* Bug #1451 - local struct field access via the address of the struct */
+
+#include
+
+typedef struct {
+ int a;
+ int b;
+} S;
+
+int failures = 0;
+
+int main(void)
+{
+ S a = {2, 5};
+ S b = {1, 4};
+ S m[1] = {{6, 3}};
+ S *p = &a;
+
+ (&a)->a += b.a;
+ p->b += b.b;
+ m->a += b.a;
+
+ if ((&a)->a != 3) {
+ ++failures;
+ printf("Expected 3, got %d\n", (&a)->a);
+ }
+
+ if (p->b != 9) {
+ ++failures;
+ printf("Expected 9, got %d\n", p->b);
+ }
+
+ if (m->a != 7) {
+ ++failures;
+ printf("Expected 7, got %d\n", m->a);
+ }
+
+ return failures;
+}
diff --git a/test/val/bug1462.c b/test/val/bug1462-biefield-assign-1.c
similarity index 100%
rename from test/val/bug1462.c
rename to test/val/bug1462-biefield-assign-1.c
diff --git a/test/val/bug1462-2.c b/test/val/bug1462-biefield-assign-2.c
similarity index 100%
rename from test/val/bug1462-2.c
rename to test/val/bug1462-biefield-assign-2.c
diff --git a/test/val/bug1462-3.c b/test/val/bug1462-biefield-assign-3.c
similarity index 100%
rename from test/val/bug1462-3.c
rename to test/val/bug1462-biefield-assign-3.c
diff --git a/test/val/bug1462-4.c b/test/val/bug1462-biefield-assign-4.c
similarity index 95%
rename from test/val/bug1462-4.c
rename to test/val/bug1462-biefield-assign-4.c
index f811ddbd6..e607dbd25 100644
--- a/test/val/bug1462-4.c
+++ b/test/val/bug1462-biefield-assign-4.c
@@ -1,6 +1,6 @@
/* issue #1462 - Bit-fields are still broken */
-/* More tests on "op= expression result value" that a naive fix might fail with */
+/* When (un-)signedness involves with integral promotion */
#include
#include
diff --git a/test/val/bug1643.c b/test/val/bug1643.c
index eba733511..c6237b7fb 100644
--- a/test/val/bug1643.c
+++ b/test/val/bug1643.c
@@ -1,12 +1,12 @@
-/* bug #1643, macro expansion in #include */
-
-#define MKSTR(a) MKSTR_IMPL(a)
-#define MKSTR_IMPL(a) #a
-#define BUG1643_H bug1643.h
-
-#include MKSTR(BUG1643_H)
-
-int main(void)
-{
- return BUG1643_RESULT;
-}
+/* bug #1643, macro expansion in #include */
+
+#define MKSTR(a) MKSTR_IMPL(a)
+#define MKSTR_IMPL(a) #a
+#define BUG1643_H bug1643.h
+
+#include MKSTR(BUG1643_H)
+
+int main(void)
+{
+ return BUG1643_RESULT;
+}
diff --git a/test/val/bug1643.h b/test/val/bug1643.h
index fe0423688..068263436 100644
--- a/test/val/bug1643.h
+++ b/test/val/bug1643.h
@@ -1,13 +1,13 @@
-/* bug #1643, macro expansion in #include */
-
-#define STDIO_H
-#include STDIO_H
-
-#ifdef string
-#undef string
-#endif
-
-#define string 0!%^&*/_=
-#include
-
-#define BUG1643_RESULT 0
+/* bug #1643, macro expansion in #include */
+
+#define STDIO_H
+#include STDIO_H
+
+#ifdef string
+#undef string
+#endif
+
+#define string 0!%^&*/_=
+#include
+
+#define BUG1643_RESULT 0
diff --git a/test/val/bug1690.c b/test/val/bug1690.c
index 499dc6b35..78c0cda41 100644
--- a/test/val/bug1690.c
+++ b/test/val/bug1690.c
@@ -1,30 +1,30 @@
-/* OptCmp1 messed up with labels */
-
-#include
-
-static int failures = 0;
-static unsigned int z = 0xFF23;
-
-int main(void)
-{
- register unsigned int x = 0x200;
- register unsigned int y = 0;
-
- do {
- ++y;
- } while (--x);
- if (y != 0x200) {
- printf("y should be 0x200, not 0x%X.\n", y);
- ++failures;;
- }
-
- if ((z -= 0x23)) {
- /* Passed -- non-zero z looks like non-zero. */
- } else {
- /* Failed -- only the low byte of z was tested. */
- printf("Test thinks non-zero z is zero.\n");
- ++failures;
- }
-
- return failures;
-}
+/* OptCmp1 messed up with labels */
+
+#include
+
+static int failures = 0;
+static unsigned int z = 0xFF23;
+
+int main(void)
+{
+ register unsigned int x = 0x200;
+ register unsigned int y = 0;
+
+ do {
+ ++y;
+ } while (--x);
+ if (y != 0x200) {
+ printf("y should be 0x200, not 0x%X.\n", y);
+ ++failures;;
+ }
+
+ if ((z -= 0x23)) {
+ /* Passed -- non-zero z looks like non-zero. */
+ } else {
+ /* Failed -- only the low byte of z was tested. */
+ printf("Test thinks non-zero z is zero.\n");
+ ++failures;
+ }
+
+ return failures;
+}
diff --git a/test/val/bug1822-pptest.c b/test/val/bug1822-pptest.c
index eb4d23391..133d69f6b 100644
--- a/test/val/bug1822-pptest.c
+++ b/test/val/bug1822-pptest.c
@@ -1,25 +1,25 @@
-/* Bug #1822 - Redefined macros failed to be all undefined with a single #undef */
-
-#undef F
-#undef F
-
-#define F 1
-#define F 1
-
-#undef F
-#if defined F
-#error #undef F fails!
-#endif
-
-#define F 0
-
-#include
-
-int main(void)
-{
- if (F != 0)
- {
- printf("failed: F = %d\n", F);
- }
- return F;
-}
+/* Bug #1822 - Redefined macros failed to be all undefined with a single #undef */
+
+#undef F
+#undef F
+
+#define F 1
+#define F 1
+
+#undef F
+#if defined F
+#error #undef F fails!
+#endif
+
+#define F 0
+
+#include
+
+int main(void)
+{
+ if (F != 0)
+ {
+ printf("failed: F = %d\n", F);
+ }
+ return F;
+}
diff --git a/test/val/bug1838.c b/test/val/bug1838.c
index ba3c1164f..38becf5e9 100644
--- a/test/val/bug1838.c
+++ b/test/val/bug1838.c
@@ -1,35 +1,35 @@
-/* Bug 1838 - function parameters declared as function types rather than function pointers */
-
-#include
-
-static int failures = 0;
-
-typedef int fn_t(int);
-
-int main(void)
-{
- void foo(fn_t*);
- fn_t bar;
-
- foo(bar);
- return 0;
-}
-
-void foo(int func(int))
-{
- int n = func(42);
-
- if (n != 12) {
- printf("n = %d, expected: 12\n", n);
- ++failures;
- }
-}
-
-int bar(int a)
-{
- if (a != 42) {
- printf("a = %d, expected: 42\n", a);
- ++failures;
- }
- return 12;
-}
+/* Bug 1838 - function parameters declared as function types rather than function pointers */
+
+#include
+
+static int failures = 0;
+
+typedef int fn_t(int);
+
+int main(void)
+{
+ void foo(fn_t*);
+ fn_t bar;
+
+ foo(bar);
+ return failures;
+}
+
+void foo(int func(int))
+{
+ int n = func(42);
+
+ if (n != 12) {
+ printf("n = %d, expected: 12\n", n);
+ ++failures;
+ }
+}
+
+int bar(int a)
+{
+ if (a != 42) {
+ printf("a = %d, expected: 42\n", a);
+ ++failures;
+ }
+ return 12;
+}
diff --git a/test/val/bug1847-struct-field-access.c b/test/val/bug1847-struct-field-access.c
new file mode 100644
index 000000000..f7e19e40b
--- /dev/null
+++ b/test/val/bug1847-struct-field-access.c
@@ -0,0 +1,46 @@
+/* Bug #1847 - struct field access */
+
+#include
+
+struct TestStruct {
+ char a;
+ char b;
+ char c;
+};
+
+struct TestStruct s0[2] = { {0xFF, 0, 0xFF}, {0, 0x42, 0xFF} };
+struct TestStruct* s0Ptr = s0;
+
+#define TEST_READ_SUB(X, E) \
+ if ((X) != (E)) { \
+ printf(#X ": 0x%X, expected: 0x%X\n", (X), (E)); \
+ ++failures; \
+ }
+
+#define TEST_READ(S, I, F, E) \
+ TEST_READ_SUB(S[I].F, E) \
+ TEST_READ_SUB((&S[I])->F, E) \
+ TEST_READ_SUB((&S[I])[0].F, E) \
+ TEST_READ_SUB(S##Ptr[I].F, E) \
+ TEST_READ_SUB((&S##Ptr[I])->F, E) \
+ TEST_READ_SUB((&(S##Ptr[I]))[0].F, E) \
+ TEST_READ_SUB((&(*S##Ptr))[I].F, E) \
+ TEST_READ_SUB((&(*S##Ptr)+I)->F, E) \
+ TEST_READ_SUB((S##Ptr+I)->F, E) \
+ TEST_READ_SUB((S##Ptr+I)[0].F, E)
+
+static unsigned failures = 0;
+
+int main(void) {
+ struct TestStruct s1[2] = { {0xFF, 0, 0xFF}, {0, 42, 0xFF} };
+ struct TestStruct* s1Ptr = s1;
+
+ TEST_READ(s0, 1, b, 0x42)
+ TEST_READ(s1, 1, b, 42)
+
+ if (failures > 0) {
+ printf("Failures: %u\n", failures);
+ }
+
+ return failures;
+}
diff --git a/test/val/bug1853-inline-asm.c b/test/val/bug1853-inline-asm.c
new file mode 100644
index 000000000..9813566dc
--- /dev/null
+++ b/test/val/bug1853-inline-asm.c
@@ -0,0 +1,21 @@
+
+/* #1853 - Regression on inline assembly expression evaluation */
+
+int main(void)
+{
+/*
+compiles with e.g. Git 2f4e2a3 to the expected
+
+ lda 1
+ lda 1 + 1
+ rts
+
+However, with the current HEAD, it compiles to
+
+ lda 1
+ lda
+*/
+ __asm__("lda 1");
+ __asm__("lda 1 + 1");
+ return 0;
+}
diff --git a/test/val/bug1891.c b/test/val/bug1891.c
new file mode 100644
index 000000000..0373ba46d
--- /dev/null
+++ b/test/val/bug1891.c
@@ -0,0 +1,19 @@
+/* bug #1891 - backslash/newline sequence in string constants is treated wrong */
+
+#include
+#include
+
+const char* a = "hello \
+world";
+const char* b = \
+"hello world";
+
+int main(void)
+{
+ if (strcmp(a, b) != 0) {
+ printf("a:\n%s\n", a);
+ printf("b:\n%s\n", b);
+ return 1;
+ }
+ return 0;
+}
diff --git a/test/val/bool3.c b/test/val/compare-bool3.c
similarity index 100%
rename from test/val/bool3.c
rename to test/val/compare-bool3.c
diff --git a/test/val/const-side-effect.c b/test/val/const-side-effect.c
new file mode 100644
index 000000000..cebc6f099
--- /dev/null
+++ b/test/val/const-side-effect.c
@@ -0,0 +1,160 @@
+/* Check code generation for constant operands with side-effects */
+
+#include
+
+static int failures = 0;
+
+#define TEST(X, Y, L) \
+ if (x != X || y != Y) { \
+ printf("Failed: " L "\nExpected: x = " #X ", y = " #Y ", got: x = %d, y = %d\n\n", x, y); \
+ ++failures; \
+ }
+
+#define TEST_LINE_UNARY(OP, RH, ID) \
+ "x = " #OP "(set(&y, " #ID "), " #RH ")"
+
+#define TEST_UNARY(OP, RH, RS, ID) \
+ x = -!(RS), y = -!(RS); \
+ x = OP (set(&y, ID), RH); \
+ TEST(RS, ID, TEST_LINE_UNARY(OP, RH, ID))
+
+#define TEST_LINE_RHS_EFFECT(LH, OP, RH, ID) \
+ "x = " #LH " " #OP " (set(&y, " #ID "), " #RH ")"
+
+#define TEST_LINE_LHS_EFFECT(LH, OP, RH, ID) \
+ "y = (set(&x, " #ID "), " #LH ") " #OP " " #RH
+
+#define TEST_BINARY(LH, OP, RH, RS, ID) \
+ x = -!(RS), y = -!(RS); \
+ x = LH OP (set(&y, ID), RH); \
+ TEST(RS, ID, TEST_LINE_RHS_EFFECT(LH, OP, RH, ID)) \
+ y = -!(RS), x = -!(RS); \
+ y = (set(&x, ID), LH) OP RH; \
+ TEST(ID, RS, TEST_LINE_LHS_EFFECT(LH, OP, RH, ID)) \
+ y = -!(RS); \
+ x = (set(&x, LH), x) OP (set(&y, ID), RH); \
+ TEST(RS, ID, TEST_LINE_RHS_EFFECT((set(&x, LH), x), OP, RH, ID)) \
+ x = -!(RS); \
+ y = (set(&x, ID), LH) OP (set(&y, RH), y); \
+ TEST(ID, RS, TEST_LINE_LHS_EFFECT(LH, OP, (set(&y, RH), y), ID))
+
+#define TEST_LINE_RHS_EFFECT_WITH_CAST(LT, LH, OP, RT, RH, ID) \
+ "x = (" #LT ")" #LH " " #OP " (" #RT ")(set(&y, " #ID "), " #RH ")"
+
+#define TEST_LINE_LHS_EFFECT_WITH_CAST(LT, LH, OP, RT, RH, ID) \
+ "y = (" #LT ")(set(&x, " #ID "), " #LH ") " #OP " (" #RT ")" #RH
+
+#define TEST_BINARY_WITH_CAST(LT, LH, OP, RT, RH, RS, ID) \
+ x = -!(RS), y = -!(RS); \
+ x = (LT)LH OP (RT)(set(&y, ID), RH); \
+ TEST(RS, ID, TEST_LINE_RHS_EFFECT_WITH_CAST(LT, LH, OP, RT, RH, ID)) \
+ y = -!(RS), x = -!(RS); \
+ y = (LT)(set(&x, ID), LH) OP (RT)RH; \
+ TEST(ID, RS, TEST_LINE_LHS_EFFECT_WITH_CAST(LT, LH, OP, RT, RH, ID)) \
+ y = -!(RS); \
+ x = (LT)(set(&x, LH), x) OP (RT)(set(&y, ID), RH); \
+ TEST(RS, ID, TEST_LINE_RHS_EFFECT_WITH_CAST(LT, (set(&x, LH), x), OP, RT, RH, ID)) \
+ x = -!(RS); \
+ y = (LT)(set(&x, ID), LH) OP (RT)(set(&y, RH), y); \
+ TEST(ID, RS, TEST_LINE_LHS_EFFECT_WITH_CAST(LT, LH, OP, RT, (set(&y, RH), y), ID))
+
+void set(int *p, int q)
+{
+ *p = q;
+}
+
+int twice(int a)
+{
+ return a * 2;
+}
+
+int (*twicep)(int) = twice;
+
+void test_unary(void)
+{
+ int x, y;
+
+ TEST_UNARY(+, 42, 42, 1);
+ TEST_UNARY(-, -42, 42, 2);
+ TEST_UNARY(~, ~42, 42, 3);
+ TEST_UNARY(!, 42, 0, 4);
+}
+
+void test_binary_arithmetic(void)
+{
+ int x, y;
+
+ TEST_BINARY(41, +, 1, 42, 1)
+ TEST_BINARY(42, +, 0, 42, 1)
+
+ TEST_BINARY(43, -, 1, 42, 2)
+ TEST_BINARY(42, -, 0, 42, 2)
+
+ TEST_BINARY(6, *, 7, 42, 3)
+ TEST_BINARY(42, *, 1, 42, 3)
+ TEST_BINARY(-42, *, -1, 42, 3)
+
+ TEST_BINARY(126, /, 3, 42, 4)
+ TEST_BINARY(42, /, 1, 42, 4)
+ TEST_BINARY(-42, /, -1, 42, 4)
+
+ TEST_BINARY(85, %, 43, 42, 5)
+ TEST_BINARY(10794, %, 256, 42, 5)
+
+ TEST_BINARY(84, >>, 1, 42, 6)
+ TEST_BINARY(42, >>, 0, 42, 6)
+ TEST_BINARY(10752, >>, 8, 42, 6)
+ TEST_BINARY(21504, >>, 9, 42, 6)
+
+ TEST_BINARY(21, <<, 1, 42, 7)
+ TEST_BINARY(42, <<, 0, 42, 7)
+ TEST_BINARY(42, <<, 8, 10752, 7)
+
+ TEST_BINARY(59, &, 238, 42, 8)
+ TEST_BINARY(42, &, 0, 0, 8)
+ TEST_BINARY(42, &, -1, 42, 8)
+
+ TEST_BINARY(34, |, 10, 42, 9)
+ TEST_BINARY(42, |, 0, 42, 9)
+ TEST_BINARY(34, |, -1, -1, 9)
+
+ TEST_BINARY(59, ^, 17, 42, 10)
+ TEST_BINARY(42, ^, 0, 42, 10)
+ TEST_BINARY(~42, ^, -1, 42, 10)
+}
+
+void test_binary_comparison(void)
+{
+ int x, y;
+
+ TEST_BINARY(42, ==, 42, 1, 11)
+
+ TEST_BINARY(42, !=, 43, 1, 12)
+ TEST_BINARY_WITH_CAST(signed char, 42, !=, long, 65536L, 1, 12)
+ TEST_BINARY_WITH_CAST(long, 65536L, !=, signed char, 42, 1, 12)
+
+ TEST_BINARY(42, >, 41, 1, 13)
+ TEST_BINARY_WITH_CAST(int, 0, >, unsigned, 42, 0, 13)
+
+ TEST_BINARY(42, <, 43, 1, 14)
+ TEST_BINARY_WITH_CAST(unsigned, 42, <, int, 0, 0, 14)
+
+ TEST_BINARY(42, >=, 0, 1, 15)
+ TEST_BINARY_WITH_CAST(unsigned, 42, >=, int, 0, 1, 15)
+
+ TEST_BINARY(42, <=, 43, 1, 16)
+ TEST_BINARY_WITH_CAST(int, 0, <=, unsigned, 42, 1, 16)
+}
+
+int main(void)
+{
+ test_unary();
+ test_binary_arithmetic();
+ test_binary_comparison();
+
+ if (failures != 0) {
+ printf("Failures: %d\n", failures);
+ }
+
+ return failures;
+}
diff --git a/test/val/counter.c b/test/val/counter.c
index 4efa18359..1867b1a66 100644
--- a/test/val/counter.c
+++ b/test/val/counter.c
@@ -1,60 +1,60 @@
-/* Tests for predefined macro __COUNTER__ */
-
-#include
-
-static int failures = 0;
-
-#if __COUNTER__ /* 0 */
-# error __COUNTER__ should begin at 0!
-#elif __COUNTER__ == 1 /* 1 */
-# define CONCAT(a,b) CONCAT_impl_(a,b)
-# define CONCAT_impl_(a,b) a##b
-#endif
-
-#line 42 "What is the answer?"
-int CONCAT(ident,__COUNTER__)[0+__LINE__] = {__LINE__}, CONCAT(ident,__COUNTER__)[0+__LINE__] = {__LINE__}; /* 2,3 */
-
-#if __COUNTER__ == 4 ? 1 || __COUNTER__ : 0 && __COUNTER__ /* 4,5,6 */
-_Static_assert(__COUNTER__ == 7, "__COUNTER__ should be 7 here!"); /* 7 */
-# define GET_COUNTER() __COUNTER__
-# define GET_LINE() __LINE__
-# warning __COUNTER__ in #warning is just output as text and will never increase!
-#else
-# if __COUNTER__ + __COUNTER__ + __COUNTER__ /* Skipped as a whole and not incrementing */
-# endif
-# error __COUNTER__ is skipped along with the whole #error line and will never increase anyways! */
-#endif
-
-#include "counter.h"
-#include "counter.h"
-
-_Static_assert(GET_COUNTER() == 10, "__COUNTER__ should be 10 here!"); /* 10 */
-
-int main(void)
-{
- if (ident2[0] != 42) {
- printf("Expected ident2[0]: %s, got: %s\n", 42, ident2[0]);
- ++failures;
- }
-
- if (ident3[0] != 42) {
- printf("Expected ident3[0]: %s, got: %s\n", 42, ident3[0]);
- ++failures;
- }
-
- if (ident8 != 8) {
- printf("Expected ident8: %s, got: %s\n", 8, ident8);
- ++failures;
- }
-
- if (ident9 != 9) {
- printf("Expected ident9: %s, got: %s\n", 9, ident9);
- ++failures;
- }
-
- if (failures != 0) {
- printf("Failures: %d\n", failures);
- }
-
- return failures;
-}
+/* Tests for predefined macro __COUNTER__ */
+
+#include
+
+static int failures = 0;
+
+#if __COUNTER__ /* 0 */
+# error __COUNTER__ should begin at 0!
+#elif __COUNTER__ == 1 /* 1 */
+# define CONCAT(a,b) CONCAT_impl_(a,b)
+# define CONCAT_impl_(a,b) a##b
+#endif
+
+#line 42 "What is the answer?"
+int CONCAT(ident,__COUNTER__)[0+__LINE__] = {__LINE__}, CONCAT(ident,__COUNTER__)[0+__LINE__] = {__LINE__}; /* 2,3 */
+
+#if __COUNTER__ == 4 ? 1 || __COUNTER__ : 0 && __COUNTER__ /* 4,5,6 */
+_Static_assert(__COUNTER__ == 7, "__COUNTER__ should be 7 here!"); /* 7 */
+# define GET_COUNTER() __COUNTER__
+# define GET_LINE() __LINE__
+# warning __COUNTER__ in #warning is just output as text and will never increase!
+#else
+# if __COUNTER__ + __COUNTER__ + __COUNTER__ /* Skipped as a whole and not incrementing */
+# endif
+# error __COUNTER__ is skipped along with the whole #error line and will never increase anyways! */
+#endif
+
+#include "counter.h"
+#include "counter.h"
+
+_Static_assert(GET_COUNTER() == 10, "__COUNTER__ should be 10 here!"); /* 10 */
+
+int main(void)
+{
+ if (ident2[0] != 42) {
+ printf("Expected ident2[0]: %s, got: %s\n", 42, ident2[0]);
+ ++failures;
+ }
+
+ if (ident3[0] != 42) {
+ printf("Expected ident3[0]: %s, got: %s\n", 42, ident3[0]);
+ ++failures;
+ }
+
+ if (ident8 != 8) {
+ printf("Expected ident8: %s, got: %s\n", 8, ident8);
+ ++failures;
+ }
+
+ if (ident9 != 9) {
+ printf("Expected ident9: %s, got: %s\n", 9, ident9);
+ ++failures;
+ }
+
+ if (failures != 0) {
+ printf("Failures: %d\n", failures);
+ }
+
+ return failures;
+}
diff --git a/test/val/counter.h b/test/val/counter.h
index b6b5a98e2..b97cbf54d 100644
--- a/test/val/counter.h
+++ b/test/val/counter.h
@@ -1,4 +1,4 @@
-/* Tests for predefined macro __COUNTER__ */
-
-#line GET_COUNTER() /* 1st: 8; 2nd: 9 */
-int CONCAT(ident,GET_LINE()) = GET_LINE();
+/* Tests for predefined macro __COUNTER__ */
+
+#line GET_COUNTER() /* 1st: 8; 2nd: 9 */
+int CONCAT(ident,GET_LINE()) = GET_LINE();
diff --git a/test/val/decl-extern-shadow.c b/test/val/decl-extern-shadow.c
new file mode 100644
index 000000000..6df3c9d50
--- /dev/null
+++ b/test/val/decl-extern-shadow.c
@@ -0,0 +1,31 @@
+/* Test for shadowing and linkage of file-scope "static" and block-scope "extern" declarations */
+
+static int g(int x); /* Generated functions with internal linkage are not always kept in cc65 */
+
+int main(void)
+{
+ char f = 'f'; /* Shadows global "int f(void)" (if any) */
+ char c = 'c'; /* Shadows global "int c" (if any) */
+ {
+ void* f = 0; /* Shadows local "char f" */
+ void* c = 0; /* Shadows local "char c" */
+ {
+ int f(void); /* Shadows local "char f" and "void* f" */
+ extern int g(int); /* Shadows global "int g(int x)" */
+ extern int c; /* Shadows local "char c" and "void* c" */
+ return f() ^ g(c); /* Link to global "int g(int x)" */
+ }
+ }
+}
+
+int c = 42;
+
+int f(void)
+{
+ return 42;
+}
+
+int g(int x)
+{
+ return x;
+}
diff --git a/test/val/decl-mixed-specifiers.c b/test/val/decl-mixed-specifiers.c
new file mode 100644
index 000000000..a0fb1596b
--- /dev/null
+++ b/test/val/decl-mixed-specifiers.c
@@ -0,0 +1,19 @@
+/* bug 1888 - cc65 fails with storage class specifiers after type specifiers */
+
+#include
+
+int const typedef volatile x_type, * const volatile y_type;
+
+int static failures = 0;
+
+int extern main(void);
+
+int main(void)
+{
+ volatile static x_type const x = 42, * const volatile y[] = { 1 ? &x : (y_type)0 };
+ if (**y != 42) {
+ ++failures;
+ printf("y = %d, Expected: 42\n", **y);
+ }
+ return failures;
+}
diff --git a/test/val/static-1.c b/test/val/decl-static-extern.c
similarity index 100%
rename from test/val/static-1.c
rename to test/val/decl-static-extern.c
diff --git a/test/val/static-fwd-decl.c b/test/val/decl-static-fwd.c
similarity index 100%
rename from test/val/static-fwd-decl.c
rename to test/val/decl-static-fwd.c
diff --git a/test/val/nestfor.c b/test/val/for-nested.c
similarity index 100%
rename from test/val/nestfor.c
rename to test/val/for-nested.c
diff --git a/test/val/mult1.c b/test/val/mult1.c
index 6d491a427..95141d76d 100644
--- a/test/val/mult1.c
+++ b/test/val/mult1.c
@@ -26,15 +26,17 @@ void done()
void m1(void)
{
- c1 = c1*5; /* char = char * lit */
+ c1 = c1*5; /* char = char * lit */
+ c2 = c1*c3; /* char = char * char */
- c2 = c1 *c3; /* char = char * char */
-
- uc1= uc1*5; /* uchar = uchar * lit *
- uc2=uc1*uc3; /* uchar = uchar * uchar */
+ uc1 = uc1*3; /* uchar = uchar * lit */
+ uc2 = uc1*uc3; /* uchar = uchar * uchar */
if(c2 != 25)
failures++;
+
+ if(uc2 != 36)
+ failures++;
}
void m2(unsigned char uc)
@@ -96,6 +98,9 @@ int main(void)
c1 = 1;
c3 = 5;
+ uc1 = 2;
+ uc3 = 6;
+
m1();
uc1 = 0x10;
@@ -107,7 +112,7 @@ int main(void)
ui3 = ui1*ui2; /* uint = uint * unit */
- /*m3(TESTLIT);*/
+ m3(TESTLIT);
success = failures;
done();
diff --git a/test/val/opsize.c b/test/val/opsize.c
index 20c7f0511..8ec49e8a8 100644
--- a/test/val/opsize.c
+++ b/test/val/opsize.c
@@ -1,33 +1,33 @@
-
-/* Test for result types of certain unary operations */
-
-#include
-
-signed char x;
-struct S {
- unsigned char a : 3;
- unsigned int b : 3;
-} s;
-
-int main(void)
-{
- _Static_assert(sizeof (++x) == sizeof (char), "++x result should not have promoted type");
- _Static_assert(sizeof (--x) == sizeof (char), "--x result should not have promoted type");
- _Static_assert(sizeof (x++) == sizeof (char), "x++ result should not have promoted type");
- _Static_assert(sizeof (x--) == sizeof (char), "x-- result should not have promoted type");
- _Static_assert(sizeof (x=0) == sizeof (char), "x=0 result should not have promoted type");
-
- _Static_assert(sizeof (+x) == sizeof (int), "+x result should have promoted type");
- _Static_assert(sizeof (-x) == sizeof (int), "-x result should have promoted type");
- _Static_assert(sizeof (~x) == sizeof (int), "~x result should have promoted type");
-
- _Static_assert(sizeof (+s.a) == sizeof (int), "+s.a result should have promoted type");
- _Static_assert(sizeof (-s.a) == sizeof (int), "-s.a result should have promoted type");
- _Static_assert(sizeof (~s.a) == sizeof (int), "~s.a result should have promoted type");
-
- _Static_assert(sizeof (+s.b) == sizeof (int), "+s.b result should have promoted type");
- _Static_assert(sizeof (-s.b) == sizeof (int), "-s.b result should have promoted type");
- _Static_assert(sizeof (~s.b) == sizeof (int), "~s.b result should have promoted type");
-
- return 0;
-}
+
+/* Test for result types of certain unary operations */
+
+#include
+
+signed char x;
+struct S {
+ unsigned char a : 3;
+ unsigned int b : 3;
+} s;
+
+int main(void)
+{
+ _Static_assert(sizeof (++x) == sizeof (char), "++x result should not have promoted type");
+ _Static_assert(sizeof (--x) == sizeof (char), "--x result should not have promoted type");
+ _Static_assert(sizeof (x++) == sizeof (char), "x++ result should not have promoted type");
+ _Static_assert(sizeof (x--) == sizeof (char), "x-- result should not have promoted type");
+ _Static_assert(sizeof (x=0) == sizeof (char), "x=0 result should not have promoted type");
+
+ _Static_assert(sizeof (+x) == sizeof (int), "+x result should have promoted type");
+ _Static_assert(sizeof (-x) == sizeof (int), "-x result should have promoted type");
+ _Static_assert(sizeof (~x) == sizeof (int), "~x result should have promoted type");
+
+ _Static_assert(sizeof (+s.a) == sizeof (int), "+s.a result should have promoted type");
+ _Static_assert(sizeof (-s.a) == sizeof (int), "-s.a result should have promoted type");
+ _Static_assert(sizeof (~s.a) == sizeof (int), "~s.a result should have promoted type");
+
+ _Static_assert(sizeof (+s.b) == sizeof (int), "+s.b result should have promoted type");
+ _Static_assert(sizeof (-s.b) == sizeof (int), "-s.b result should have promoted type");
+ _Static_assert(sizeof (~s.b) == sizeof (int), "~s.b result should have promoted type");
+
+ return 0;
+}
diff --git a/test/val/ppshift.c b/test/val/ppshift.c
new file mode 100644
index 000000000..466b15926
--- /dev/null
+++ b/test/val/ppshift.c
@@ -0,0 +1,120 @@
+/*
+ Test of bitwise-shift in preprocessor expressions.
+
+ Note: Keep in mind that integer constants are always 32-bit in PP for cc65.
+*/
+
+/* Signed lhs */
+#if 1 << 16 != 0x00010000
+#error 1 << 16 != 0x00010000
+#endif
+
+#if 0x00010000 << -16 != 1
+#error 0x00010000 << -16 != 1
+#endif
+
+#if 0x10000 >> 16 != 1
+#error 0x10000 >> 16 != 1
+#endif
+
+#if 1 >> -16 != 0x10000
+#error 1 >> -16 != 0x10000
+#endif
+
+#if 1 << 32 != 0
+#error 1 << 32 != 0
+#endif
+
+#if 1 << -32 != 0
+#error 1 << -32 != 0
+#endif
+
+#if 1 >> 32 != 0
+#error 1 >> 32 != 0
+#endif
+
+#if 1 >> -32 != 0
+#error 1 >> -32 != 0
+#endif
+
+#if -1 << 32 != 0
+#error -1 << 32 != 0
+#endif
+
+#if -1 << -32 != -1
+#error -1 << -32 != -1
+#endif
+
+#if -1 >> 32 != -1
+#error -1 >> 32 != -1
+#endif
+
+#if -1 >> -32 != 0
+#error -1 >> -32 != 0
+#endif
+
+/* NOTE: 2147483648 is an UNSIGNED integer! */
+#if -1 << 2147483648 != 0
+#error -1 << 2147483648 != 0
+#endif
+
+/* NOTE: -2147483648 is also an UNSIGNED integer! */
+#if -1 << -2147483648 != 0
+#error -1 << -2147483648 != 0
+#endif
+
+#if -1 << (-2147483647 - 1) != -1
+#error -1 << (-2147483647 - 1) != -1
+#endif
+
+/* NOTE: 2147483648 is an UNSIGNED integer! */
+#if -1 >> 2147483648 != -1
+#error -1 >> 2147483648 != -1
+#endif
+
+/* NOTE: -2147483648 is also an UNSIGNED integer! */
+#if -1 >> -2147483648 != -1
+#error -1 >> -2147483648 != 0
+#endif
+
+#if -1 >> (-2147483647 - 1) != 0
+#error -1 >> (-2147483647 - 1) != 0
+#endif
+
+/* Unsigned lhs */
+#if 1U << 16 != 0x00010000
+#error 1U << 16 != 0x00010000
+#endif
+
+#if 0x80000000U << -16 != 0x8000
+#error 0x80000000U << -16 != 0x8000
+#endif
+
+#if 0x80000000U >> 16 != 0x8000
+#error 0x80000000U >> 16 != 0x8000
+#endif
+
+#if 1U >> -16 != 0x10000
+#error 1U >> -16 != 0x10000
+#endif
+
+#if -1U << 32 != 0
+#error -1U << 32 != 0
+#endif
+
+#if -1U << -32 != 0
+#error -1U << -32 != 0
+#endif
+
+#if -1U >> 32 != 0
+#error -1U >> 32 != 0
+#endif
+
+#if -1U >> -32 != 0
+#error -1U >> -32 != 0
+#endif
+
+int main(void)
+{
+ return 0;
+}
diff --git a/test/val/pr1833.c b/test/val/pr1833.c
index bdc820811..177069eb4 100644
--- a/test/val/pr1833.c
+++ b/test/val/pr1833.c
@@ -1,13 +1,13 @@
-/* Test for PR #1833 fixes */
-
-#define char 1
-
-#if char && !int && L'A' - L'B' == 'A' - 'B' && L'A' == 'A'
-#else
-#error
-#endif
-
-int main(void)
-{
- return 0;
-}
+/* Test for PR #1833 fixes */
+
+#define char 1
+
+#if char && !int && L'A' - L'B' == 'A' - 'B' && L'A' == 'A'
+#else
+#error
+#endif
+
+int main(void)
+{
+ return 0;
+}
diff --git a/test/val/staticassert.c b/test/val/staticassert.c
index e43eeec8d..3338f7a4a 100644
--- a/test/val/staticassert.c
+++ b/test/val/staticassert.c
@@ -65,6 +65,13 @@ struct S {
int b;
};
+/* _Static_assert can also appear in unions. */
+union U {
+ int a;
+ _Static_assert (1, "1 should still be true.");
+ int b;
+};
+
int main (void)
{
diff --git a/test/val/anon-struct1.c b/test/val/struct-anon1.c
similarity index 100%
rename from test/val/anon-struct1.c
rename to test/val/struct-anon1.c
diff --git a/test/val/anon-struct2.c b/test/val/struct-anon2.c
similarity index 100%
rename from test/val/anon-struct2.c
rename to test/val/struct-anon2.c
diff --git a/test/val/return-struct.c b/test/val/struct-returned.c
similarity index 100%
rename from test/val/return-struct.c
rename to test/val/struct-returned.c
diff --git a/test/val/casttochar.c b/test/val/type-cast-to-char.c
similarity index 100%
rename from test/val/casttochar.c
rename to test/val/type-cast-to-char.c
diff --git a/test/val/char-promote.c b/test/val/type-char-promote.c
similarity index 100%
rename from test/val/char-promote.c
rename to test/val/type-char-promote.c
diff --git a/test/val/uneval.c b/test/val/uneval.c
index 50e00973a..fe42cc592 100644
--- a/test/val/uneval.c
+++ b/test/val/uneval.c
@@ -1,46 +1,46 @@
-/*
- Copyright 2021, 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 deferred operations in unevaluated context resulted from 'sizeof' and
- short-circuited code-paths in AND, OR and tenary operations.
-
- https://github.com/cc65/cc65/issues/1406
-*/
-
-#include
-
-int main(void)
-{
- int i = 0;
- int j = 0;
-
- sizeof(i++ | j--);
- 0 && (i++ | j--);
- 1 || (i++ | j--);
- 0 ? i++ | j-- : 0;
- 1 ? 0 : i++ | j--;
-
- if (i != 0 || j != 0) {
- printf("i = %d, j = %d\n", i, j);
- printf("Failures: %d\n", i - j);
- }
- return i - j;
-}
+/*
+ Copyright 2021, 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 deferred operations in unevaluated context resulted from 'sizeof' and
+ short-circuited code-paths in AND, OR and tenary operations.
+
+ https://github.com/cc65/cc65/issues/1406
+*/
+
+#include
+
+int main(void)
+{
+ int i = 0;
+ int j = 0;
+
+ sizeof(i++ | j--);
+ 0 && (i++ | j--);
+ 1 || (i++ | j--);
+ 0 ? i++ | j-- : 0;
+ 1 ? 0 : i++ | j--;
+
+ if (i != 0 || j != 0) {
+ printf("i = %d, j = %d\n", i, j);
+ printf("Failures: %d\n", i - j);
+ }
+ return i - j;
+}
diff --git a/util/atari/Makefile b/util/atari/Makefile
index db4226f69..e53c837aa 100644
--- a/util/atari/Makefile
+++ b/util/atari/Makefile
@@ -5,6 +5,16 @@ ifdef CROSS_COMPILE
$(info CC: $(CC))
endif
+ifneq ($(shell echo),)
+ CMD_EXE = 1
+endif
+
+ifdef CMD_EXE
+ DEL = -del /f
+else
+ DEL = $(RM)
+endif
+
CFLAGS += -O3 -Wall -Wextra -Wno-char-subscripts $(USER_CFLAGS)
.PHONY: mostlyclean clean
@@ -15,6 +25,6 @@ ataricvt: ataricvt.c
$(CC) $(CFLAGS) -o ataricvt ataricvt.c
mostlyclean clean:
- $(RM) ataricvt
+ $(DEL) ataricvt
install zip:
diff --git a/util/gamate/Makefile b/util/gamate/Makefile
index db2a1f059..54fa74191 100644
--- a/util/gamate/Makefile
+++ b/util/gamate/Makefile
@@ -5,6 +5,16 @@ ifdef CROSS_COMPILE
$(info CC: $(CC))
endif
+ifneq ($(shell echo),)
+ CMD_EXE = 1
+endif
+
+ifdef CMD_EXE
+ DEL = -del /f
+else
+ DEL = $(RM)
+endif
+
CFLAGS += -O3 -Wall -Wextra -Wno-char-subscripts $(USER_CFLAGS)
.PHONY: mostlyclean clean
@@ -15,6 +25,6 @@ gamate-fixcart: gamate-fixcart.c
$(CC) $(CFLAGS) -o gamate-fixcart gamate-fixcart.c
mostlyclean clean:
- $(RM) gamate-fixcart
+ $(DEL) gamate-fixcart
install zip:
diff --git a/util/zlib/Makefile b/util/zlib/Makefile
index 3770e1f3c..f276ddaf2 100644
--- a/util/zlib/Makefile
+++ b/util/zlib/Makefile
@@ -5,21 +5,33 @@ ifdef CROSS_COMPILE
$(info CC: $(CC))
endif
+ifneq ($(shell echo),)
+ CMD_EXE = 1
+endif
+
+ifdef CMD_EXE
+ DEL = -del /f
+else
+ DEL = $(RM)
+endif
+
CFLAGS += -O3 -Wall -Wextra -Wno-char-subscripts $(USER_CFLAGS)
.PHONY: mostlyclean clean
-zlib: warning
+zlib:
+#zlib: warning
#zlib: deflater
warning:
- @echo "deflater needs zlib installed, use 'make deflater' to build"
+ @echo "util/zlib/deflater is no longer built by default"
+ @echo "use 'make deflater' to build if you need it"
+ @echo "note that you need zlib installed first"
deflater: deflater.c
$(CC) $(CFLAGS) -o deflater deflater.c -lz
mostlyclean clean:
- $(RM) deflater
-
-install zip:
+ $(DEL) deflater
+install zip:
diff --git a/util/zlib/readme.txt b/util/zlib/readme.txt
new file mode 100644
index 000000000..90e15871c
--- /dev/null
+++ b/util/zlib/readme.txt
@@ -0,0 +1,2 @@
+Deflater program in this directory is not built by default
+Use 'make deflater' to build. Note that you need zlib installed first