mirror of
https://github.com/cc65/cc65.git
synced 2025-02-06 12:31:12 +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:
commit
3513342445
@ -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);
|
||||
|
@ -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)
|
||||
/* 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)
|
||||
/* 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
|
||||
|
@ -287,12 +287,21 @@ 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
|
||||
|
161
src/cc65/expr.c
161
src/cc65/expr.c
@ -2177,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);
|
||||
|
||||
@ -2184,80 +2188,51 @@ 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 */
|
||||
@ -2314,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);
|
||||
@ -3913,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);
|
||||
@ -3935,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.
|
||||
*/
|
||||
@ -3943,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.
|
||||
*/
|
||||
@ -3977,26 +3961,22 @@ 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.
|
||||
*/
|
||||
@ -4022,9 +4002,6 @@ static void hieQuest (ExprDesc* Expr)
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
$(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
|
||||
$(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR)
|
||||
@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)
|
||||
$(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)
|
||||
|
@ -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);
|
||||
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 <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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user