1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-02 04:41:35 +00:00

Merge pull request #1913 from acqn/Diagnostics

[cc65] Improved diagnostics on div-by-zero/bitwise-shift in unevaluated context and overall
This commit is contained in:
Bob Andrews 2022-11-18 19:55:51 +01:00 committed by GitHub
commit 3513342445
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 387 additions and 211 deletions

View File

@ -315,12 +315,41 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op
} else if (Gen->Func == g_sub) { } else if (Gen->Func == g_sub) {
g_dec (Flags | CF_CONST, Expr2.IVal); g_dec (Flags | CF_CONST, Expr2.IVal);
} else { } else {
if (Expr2.IVal == 0) { if (!ED_IsUneval (Expr)) {
/* Check for div by zero/mod by zero */ if (Expr2.IVal == 0) {
if (Gen->Func == g_div) { /* Check for div by zero/mod by zero */
Error ("Division by zero"); if (Gen->Func == g_div) {
} else if (Gen->Func == g_mod) { Warning ("Division by zero");
Error ("Modulo operation with 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) { } else if (Gen->Func == g_sub) {
g_dec (Flags | CF_CONST, Expr2.IVal); g_dec (Flags | CF_CONST, Expr2.IVal);
} else { } else {
if (Expr2.IVal == 0) { if (!ED_IsUneval (Expr)) {
/* Check for div by zero/mod by zero */ if (Expr2.IVal == 0 && !ED_IsUneval (Expr)) {
if (Gen->Func == g_div) { /* Check for div by zero/mod by zero */
Error ("Division by zero"); if (Gen->Func == g_div) {
} else if (Gen->Func == g_mod) { Warning ("Division by zero");
Error ("Modulo operation with 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); Gen->Func (Flags | CF_CONST, Expr2.IVal);

View File

@ -203,6 +203,14 @@ unsigned long GetIntegerTypeMax (const Type* Type)
unsigned BitSizeOf (const Type* T)
/* Return the size (in bit-width) of a data type */
{
return IsTypeBitField (T) ? T->A.B.Width : CHAR_BITS * SizeOf (T);
}
unsigned SizeOf (const Type* T) unsigned SizeOf (const Type* T)
/* Compute size (in bytes) of object represented by type array */ /* Compute size (in bytes) of object represented by type array */
{ {
@ -288,6 +296,17 @@ 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) unsigned CheckedSizeOf (const Type* T)
/* Return the size (in bytes) of a data type. If the size is zero, emit an /* 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 ** error and return some valid size instead (so the rest of the compiler

View File

@ -287,12 +287,21 @@ unsigned long GetIntegerTypeMax (const Type* Type);
** The type must have a known size. ** 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); unsigned SizeOf (const Type* T);
/* Compute size (in bytes) of object represented by type array */ /* Compute size (in bytes) of object represented by type array */
unsigned PSizeOf (const Type* T); unsigned PSizeOf (const Type* T);
/* Compute size (in bytes) of pointee object */ /* 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); unsigned CheckedSizeOf (const Type* T);
/* Return the size (in bytes) of a data type. If the size is zero, emit an /* 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 ** error and return some valid size instead (so the rest of the compiler

View File

@ -2177,6 +2177,10 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */
/* Check for const operands */ /* Check for const operands */
if (lconst && rconst) { 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 */ /* Both operands are constant, remove the generated code */
RemoveCode (&Mark1); RemoveCode (&Mark1);
@ -2184,80 +2188,51 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */
Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type);
/* Handle the op differently for signed and unsigned types */ /* Handle the op differently for signed and unsigned types */
if (IsSignSigned (Expr->Type)) { switch (Tok) {
case TOK_OR:
/* Evaluate the result for signed operands */ Expr->IVal = (Val1 | Val2);
signed long Val1 = Expr->IVal; break;
signed long Val2 = Expr2.IVal; case TOK_XOR:
switch (Tok) { Expr->IVal = (Val1 ^ Val2);
case TOK_OR: break;
Expr->IVal = (Val1 | Val2); case TOK_AND:
break; Expr->IVal = (Val1 & Val2);
case TOK_XOR: break;
Expr->IVal = (Val1 ^ Val2); case TOK_STAR:
break; Expr->IVal = (Val1 * Val2);
case TOK_AND: break;
Expr->IVal = (Val1 & Val2); case TOK_DIV:
break; if (Val2 == 0) {
case TOK_STAR: if (!ED_IsUneval (Expr)) {
Expr->IVal = (Val1 * Val2); Warning ("Division by zero");
break; }
case TOK_DIV: Expr->IVal = 0xFFFFFFFF;
if (Val2 == 0) { } else {
Error ("Division by zero"); /* Handle signed and unsigned operands differently */
Expr->IVal = 0x7FFFFFFF; if (IsSignSigned (Expr->Type)) {
Expr->IVal = ((long)Val1 / (long)Val2);
} else { } else {
Expr->IVal = (Val1 / Val2); Expr->IVal = (Val1 / Val2);
} }
break; }
case TOK_MOD: break;
if (Val2 == 0) { case TOK_MOD:
Error ("Modulo operation with zero"); if (Val2 == 0) {
Expr->IVal = 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 { } else {
Expr->IVal = (Val1 % Val2); Expr->IVal = (Val1 % Val2);
} }
break; }
default: break;
Internal ("hie_internal: got token 0x%X\n", Tok); 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);
}
} }
/* Limit the calculated value to the range of its type */ /* Limit the calculated value to the range of its type */
@ -2314,10 +2289,12 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */
/* Second value is constant - check for div */ /* Second value is constant - check for div */
type |= CF_CONST; type |= CF_CONST;
rtype |= CF_CONST; rtype |= CF_CONST;
if (Tok == TOK_DIV && Expr2.IVal == 0) { if (Expr2.IVal == 0 && !ED_IsUneval (Expr)) {
Error ("Division by zero"); if (Tok == TOK_DIV) {
} else if (Tok == TOK_MOD && Expr2.IVal == 0) { Warning ("Division by zero");
Error ("Modulo operation with zero"); } else if (Tok == TOK_MOD) {
Warning ("Modulo operation with zero");
}
} }
if ((Gen->Flags & GEN_NOPUSH) != 0) { if ((Gen->Flags & GEN_NOPUSH) != 0) {
RemoveCode (&Mark2); RemoveCode (&Mark2);
@ -3913,17 +3890,6 @@ static void hieQuest (ExprDesc* Expr)
ED_Init (&Expr3); ED_Init (&Expr3);
Expr3.Flags = Flags; 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) { if (!ConstantCond) {
/* Condition codes not set, request a test */ /* Condition codes not set, request a test */
ED_RequireTest (Expr); ED_RequireTest (Expr);
@ -3935,6 +3901,15 @@ static void hieQuest (ExprDesc* Expr)
FalseLab = GetLocalLabel (); FalseLab = GetLocalLabel ();
g_falsejump (CF_NONE, FalseLab); g_falsejump (CF_NONE, FalseLab);
} else { } 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 /* Constant boolean subexpression could still have deferred inc/dec
** operations, so just flush their side-effects at this sequence point. ** operations, so just flush their side-effects at this sequence point.
*/ */
@ -3943,9 +3918,18 @@ static void hieQuest (ExprDesc* Expr)
if (Expr->IVal == 0) { if (Expr->IVal == 0) {
/* Remember the current code position */ /* Remember the current code position */
GetCodePos (&SkippedBranch); 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 /* Parse second expression. Remember for later if it is a NULL pointer
** expression, then load it into the primary. ** expression, then load it into the primary.
*/ */
@ -3977,26 +3961,22 @@ static void hieQuest (ExprDesc* Expr)
/* Jump around the evaluation of the third expression */ /* Jump around the evaluation of the third expression */
TrueLab = GetLocalLabel (); TrueLab = GetLocalLabel ();
ConsumeColon ();
g_jump (TrueLab); g_jump (TrueLab);
/* Jump here if the first expression was false */ /* Jump here if the first expression was false */
g_defcodelabel (FalseLab); g_defcodelabel (FalseLab);
} else { } else {
if (Expr->IVal == 0) { if (Expr->IVal == 0) {
/* Expr2 is unevaluated when the condition is false */
Expr2.Flags |= E_EVAL_UNEVAL;
/* Remove the load code of Expr2 */ /* Remove the load code of Expr2 */
RemoveCode (&SkippedBranch); RemoveCode (&SkippedBranch);
} else { } else {
/* Remember the current code position */ /* Remember the current code position */
GetCodePos (&SkippedBranch); GetCodePos (&SkippedBranch);
} }
ConsumeColon();
} }
ConsumeColon ();
/* Parse third expression. Remember for later if it is a NULL pointer /* Parse third expression. Remember for later if it is a NULL pointer
** expression, then load it into the primary. ** expression, then load it into the primary.
*/ */
@ -4022,9 +4002,6 @@ static void hieQuest (ExprDesc* Expr)
Expr3.Type = PtrConversion (Expr3.Type); Expr3.Type = PtrConversion (Expr3.Type);
if (ConstantCond && Expr->IVal != 0) { if (ConstantCond && Expr->IVal != 0) {
/* Expr3 is unevaluated when the condition is true */
Expr3.Flags |= E_EVAL_UNEVAL;
/* Remove the load code of Expr3 */ /* Remove the load code of Expr3 */
RemoveCode (&SkippedBranch); RemoveCode (&SkippedBranch);
} }

View File

@ -305,97 +305,60 @@ static void PPhie_internal (const token_t* Ops, /* List of generators */
if (PPEvaluationEnabled && !PPEvaluationFailed) { 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 */ /* If either side is unsigned, the result is unsigned */
Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED; Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED;
/* Handle the op differently for signed and unsigned integers */ switch (Tok) {
if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) { case TOK_OR:
Expr->IVal = (Val1 | Val2);
/* Evaluate the result for signed operands */ break;
signed long Val1 = Expr->IVal; case TOK_XOR:
signed long Val2 = Rhs.IVal; Expr->IVal = (Val1 ^ Val2);
switch (Tok) { break;
case TOK_OR: case TOK_AND:
Expr->IVal = (Val1 | Val2); Expr->IVal = (Val1 & Val2);
break; break;
case TOK_XOR: case TOK_PLUS:
Expr->IVal = (Val1 ^ Val2); Expr->IVal = (Val1 + Val2);
break; break;
case TOK_AND: case TOK_MINUS:
Expr->IVal = (Val1 & Val2); Expr->IVal = (Val1 - Val2);
break; break;
case TOK_PLUS: case TOK_MUL:
Expr->IVal = (Val1 + Val2); Expr->IVal = (Val1 * Val2);
break; break;
case TOK_MINUS: case TOK_DIV:
Expr->IVal = (Val1 - Val2); if (Val2 == 0) {
break; PPError ("Division by zero");
case TOK_MUL: Expr->IVal = 0;
Expr->IVal = (Val1 * Val2); } else {
break; /* Handle signed and unsigned operands differently */
case TOK_DIV: if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) {
if (Val2 == 0) { Expr->IVal = ((long)Val1 / (long)Val2);
PPError ("Division by zero");
Expr->IVal = 0;
} else { } else {
Expr->IVal = (Val1 / Val2); Expr->IVal = (Val1 / Val2);
} }
break; }
case TOK_MOD: break;
if (Val2 == 0) { case TOK_MOD:
PPError ("Modulo operation with zero"); if (Val2 == 0) {
Expr->IVal = 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 { } else {
Expr->IVal = (Val1 % Val2); Expr->IVal = (Val1 % Val2);
} }
break; }
default: break;
Internal ("PPhie_internal: got token 0x%X\n", Tok); 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);
}
} }
} }
} }

View File

@ -139,20 +139,34 @@ void ShiftExpr (struct ExprDesc* Expr)
/* Remove the code that pushes the rhs onto the stack. */ /* Remove the code that pushes the rhs onto the stack. */
RemoveCode (&Mark2); RemoveCode (&Mark2);
/* If the shift count is greater or equal than the bit count of /* If the shift count is greater than or equal to the width of the
** the operand, the behaviour is undefined according to the ** promoted left operand, the behaviour is undefined according to
** standard. ** 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); /* Here we simply "wrap" the shift count around the width */
Expr2.IVal &= ExprBits - 1; Expr2.IVal &= ExprBits - 1;
} else if (Expr2.IVal >= (long) ExprBits) {
Warning ("Shift count '%ld' >= width of type", Expr2.IVal);
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 /* If the shift count is zero, nothing happens. If the left hand

View File

@ -64,12 +64,6 @@ $(WORKDIR)/int-static-1888.$1.$2.prg: int-static-1888.c | $(WORKDIR)
$(if $(QUIET),echo misc/int-static-1888.$1.$2.prg) $(if $(QUIET),echo misc/int-static-1888.$1.$2.prg)
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
# should compile, but gives an error
$(WORKDIR)/bug1768.$1.$2.prg: bug1768.c | $(WORKDIR)
@echo "FIXME: " $$@ "currently does not compile."
$(if $(QUIET),echo misc/bug1768.$1.$2.prg)
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
# should compile, but gives an error # should compile, but gives an error
$(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR)
@echo "FIXME: " $$@ "currently does not compile." @echo "FIXME: " $$@ "currently does not compile."
@ -138,6 +132,14 @@ $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR)
$(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR)
$(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(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 # should compile, but then hangs in an endless loop
$(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR)
$(if $(QUIET),echo misc/endless.$1.$2.prg) $(if $(QUIET),echo misc/endless.$1.$2.prg)

View File

@ -1,14 +1,147 @@
/*
Copyright 2021-2022, The cc65 Authors
#include <stdlib.h> 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); Permission is granted to anyone to use this software for any purpose,
int b = 0 && (8 % 0); including commercial applications; and, to alter it and redistribute it
int c = 1 ? 42 : (0 % 0); freely, subject to the following restrictions:
int d = 1 || a / 0;
int e = 0 && b % 0;
int f = 1 ? 42 : (a %= 0, b /= 0);
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 <stdio.h>
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) 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;
} }