Fixed type promotion of switch case values.

This commit is contained in:
acqn 2023-10-25 22:38:21 +08:00
parent bb1b5c363e
commit df392fc104
2 changed files with 122 additions and 43 deletions

View File

@ -64,7 +64,7 @@
typedef struct SwitchCtrl SwitchCtrl;
struct SwitchCtrl {
Collection* Nodes; /* CaseNode tree */
TypeCode ExprType; /* Basic switch expression type */
const Type* ExprType; /* Switch controlling expression type */
unsigned Depth; /* Number of bytes the selector type has */
unsigned DefaultLabel; /* Label for the default branch */
@ -133,7 +133,7 @@ void SwitchStatement (void)
/* Setup the control structure, save the old and activate the new one */
SwitchData.Nodes = NewCollection ();
SwitchData.ExprType = GetUnqualTypeCode (&SwitchExpr.Type[0]);
SwitchData.ExprType = SwitchExpr.Type;
SwitchData.Depth = SizeOf (SwitchExpr.Type);
SwitchData.DefaultLabel = 0;
OldSwitch = Switch;
@ -152,7 +152,7 @@ void SwitchStatement (void)
/* Check if we had any labels */
if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) {
Warning ("No case labels");
Warning ("No reachable case labels for switch");
}
/* If the last statement did not have a break, we may have an open
@ -209,62 +209,64 @@ void CaseLabel (void)
/* Handle a case label */
{
ExprDesc CaseExpr; /* Case label expression */
long Val; /* Case label value */
unsigned CodeLabel; /* Code label for this case */
/* Skip the "case" token */
NextToken ();
/* Read the selector expression */
CaseExpr = NoCodeConstAbsIntExpr (hie1);
Val = CaseExpr.IVal;
/* Now check if we're inside a switch statement */
if (Switch != 0) {
/* Check the range of the expression */
switch (Switch->ExprType) {
const Type* CaseT = CaseExpr.Type;
long CaseVal = CaseExpr.IVal;
int OutOfRange = 0;
const char* DiagMsg = 0;
case T_SCHAR:
/* Signed char */
if (Val < -128 || Val > 127) {
Error ("Range error");
}
break;
CaseExpr.Type = IntPromotion (Switch->ExprType);
LimitExprValue (&CaseExpr, 1);
case T_UCHAR:
if (Val < 0 || Val > 255) {
Error ("Range error");
}
break;
case T_SHORT:
case T_INT:
if (Val < -32768 || Val > 32767) {
Error ("Range error");
}
break;
case T_USHORT:
case T_UINT:
if (Val < 0 || Val > 65535) {
Error ("Range error");
}
break;
case T_LONG:
case T_ULONG:
break;
default:
Internal ("Invalid type: %06lX", Switch->ExprType);
if (CaseVal != CaseExpr.IVal ||
(IsSignSigned (CaseT) != IsSignSigned (CaseExpr.Type) &&
(IsSignSigned (CaseT) ? CaseVal < 0 : CaseExpr.IVal < 0))) {
Warning (IsSignSigned (CaseT) ?
IsSignSigned (CaseExpr.Type) ?
"Case value is implicitly converted (%ld to %ld)" :
"Case value is implicitly converted (%ld to %lu)" :
IsSignSigned (CaseExpr.Type) ?
"Case value is implicitly converted (%lu to %ld)" :
"Case value is implicitly converted (%lu to %lu)",
CaseVal, CaseExpr.IVal);
}
/* Insert the case selector into the selector table */
CodeLabel = InsertCaseValue (Switch->Nodes, Val, Switch->Depth);
/* Check the range of the expression */
if (IsSignSigned (CaseExpr.Type)) {
if (CaseExpr.IVal < GetIntegerTypeMin (Switch->ExprType)) {
DiagMsg = "Case value (%ld) out of range for switch condition type";
OutOfRange = 1;
} else if (IsSignSigned (Switch->ExprType) ?
CaseExpr.IVal > (long)GetIntegerTypeMax (Switch->ExprType) :
SizeOf (CaseExpr.Type) > SizeOf (Switch->ExprType) &&
(unsigned long)CaseExpr.IVal > GetIntegerTypeMax (Switch->ExprType)) {
DiagMsg = "Case value (%ld) out of range for switch condition type";
OutOfRange = 1;
}
} else if ((unsigned long)CaseExpr.IVal > GetIntegerTypeMax (Switch->ExprType)) {
DiagMsg = "Case value (%lu) out of range for switch condition type";
OutOfRange = 1;
}
/* Define this label */
g_defcodelabel (CodeLabel);
if (OutOfRange == 0) {
/* Insert the case selector into the selector table */
unsigned CodeLabel = InsertCaseValue (Switch->Nodes, CaseExpr.IVal, Switch->Depth);
/* Define this label */
g_defcodelabel (CodeLabel);
} else {
Warning (DiagMsg, CaseExpr.IVal);
}
} else {

View File

@ -0,0 +1,77 @@
/* Bug #2019 - Type promotion in switch statements seems to be broken */
#include <limits.h>
#include <stdio.h>
unsigned failures;
int f1(void)
{
unsigned char c = 0xFF;
switch (c) {
case (signed char)0xFF: break;
case (unsigned char)0xFF: return 0;
}
return -1;
}
int f2(void)
{
signed char c = SCHAR_MIN;
switch (c) {
case (unsigned char)SCHAR_MIN: break;
case SCHAR_MIN: return 0;
}
return -1;
}
int f3(void)
{
signed int c = (int)UINT_MAX;
switch (c) {
case UINT_MAX: return 0;
}
return -1;
}
int f4(void)
{
unsigned int c = UINT_MAX;
switch (c) {
case -1L: return 0;
}
return -1;
}
int main(void)
{
if (f1())
{
++failures;
printf("f1() failed\n");
}
if (f2())
{
++failures;
printf("f2() failed\n");
}
if (f3())
{
++failures;
printf("f3() failed\n");
}
if (f4())
{
++failures;
printf("f4() failed\n");
}
if (failures > 0)
{
printf("failures: %u\n", failures);
}
return failures;
}