[cfe-commits] r41313 - in /cfe/trunk: Sema/Sema.h Sema/SemaStmt.cpp include/clang/Basic/DiagnosticKinds.def test/Sema/switch.c
Chris Lattner
sabre at nondot.org
Wed Aug 22 22:46:52 PDT 2007
Author: lattner
Date: Thu Aug 23 00:46:52 2007
New Revision: 41313
URL: http://llvm.org/viewvc/llvm-project?rev=41313&view=rev
Log:
start checking case values of switch stmts more closely. Emit overflow
warnings when converting case values to the expression type.
Modified:
cfe/trunk/Sema/Sema.h
cfe/trunk/Sema/SemaStmt.cpp
cfe/trunk/include/clang/Basic/DiagnosticKinds.def
cfe/trunk/test/Sema/switch.c
Modified: cfe/trunk/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/Sema/Sema.h?rev=41313&r1=41312&r2=41313&view=diff
==============================================================================
--- cfe/trunk/Sema/Sema.h (original)
+++ cfe/trunk/Sema/Sema.h Thu Aug 23 00:46:52 2007
@@ -21,6 +21,10 @@
#include <vector>
#include <string>
+namespace llvm {
+ class APSInt;
+}
+
namespace clang {
class ASTContext;
class Preprocessor;
@@ -427,6 +431,13 @@
/// otherwise emit a diagnostic and return true.
bool VerifyConstantArrayType(const ArrayType *ary, SourceLocation loc);
+ /// ConvertIntegerToTypeWarnOnOverflow - Convert the specified APInt to have
+ /// the specified width and sign. If an overflow occurs, detect it and emit
+ /// the specified diagnostic.
+ void ConvertIntegerToTypeWarnOnOverflow(llvm::APSInt &OldVal,
+ unsigned NewWidth, bool NewSign,
+ SourceLocation Loc, unsigned DiagID);
+
//===--------------------------------------------------------------------===//
// Extra semantic analysis beyond the C type system
private:
Modified: cfe/trunk/Sema/SemaStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/Sema/SemaStmt.cpp?rev=41313&r1=41312&r2=41313&view=diff
==============================================================================
--- cfe/trunk/Sema/SemaStmt.cpp (original)
+++ cfe/trunk/Sema/SemaStmt.cpp Thu Aug 23 00:46:52 2007
@@ -12,13 +12,13 @@
//===----------------------------------------------------------------------===//
#include "Sema.h"
-#include "clang/AST/Stmt.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/Stmt.h"
#include "clang/Parse/Scope.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Lex/IdentifierTable.h"
-
using namespace clang;
Sema::StmtResult Sema::ParseExprStmt(ExprTy *expr) {
@@ -71,7 +71,7 @@
if (RHSVal && !RHSVal->isIntegerConstantExpr(Context, &ExpLoc)) {
Diag(ExpLoc, diag::err_case_label_not_integer_constant_expr,
RHSVal->getSourceRange());
- return SubStmt;
+ RHSVal = 0; // Recover by just forgetting about it.
}
if (SwitchStack.empty()) {
@@ -146,12 +146,58 @@
}
Action::StmtResult
-Sema::StartSwitchStmt(ExprTy *Cond) {
- SwitchStmt *SS = new SwitchStmt((Expr*)Cond);
+Sema::StartSwitchStmt(ExprTy *cond) {
+ Expr *Cond = static_cast<Expr*>(cond);
+
+ // C99 6.8.4.2p5 - Integer promotions are performed on the controlling expr.
+ UsualUnaryConversions(Cond);
+
+ SwitchStmt *SS = new SwitchStmt(Cond);
SwitchStack.push_back(SS);
return SS;
}
+/// ConvertIntegerToTypeWarnOnOverflow - Convert the specified APInt to have
+/// the specified width and sign. If an overflow occurs, detect it and emit
+/// the specified diagnostic.
+void Sema::ConvertIntegerToTypeWarnOnOverflow(llvm::APSInt &Val,
+ unsigned NewWidth, bool NewSign,
+ SourceLocation Loc,
+ unsigned DiagID) {
+ // Perform a conversion to the promoted condition type if needed.
+ if (NewWidth > Val.getBitWidth()) {
+ // If this is an extension, just do it.
+ llvm::APSInt OldVal(Val);
+ Val.extend(NewWidth);
+
+ // If the input was signed and negative and the output is unsigned,
+ // warn.
+ if (!NewSign && OldVal.isSigned() && OldVal.isNegative())
+ Diag(Loc, DiagID, OldVal.toString(), Val.toString());
+
+ Val.setIsSigned(NewSign);
+ } else if (NewWidth < Val.getBitWidth()) {
+ // If this is a truncation, check for overflow.
+ llvm::APSInt ConvVal(Val);
+ ConvVal.trunc(NewWidth);
+ ConvVal.extend(Val.getBitWidth());
+ if (ConvVal != Val)
+ Diag(Loc, DiagID, Val.toString(), ConvVal.toString());
+
+ // Regardless of whether a diagnostic was emitted, really do the
+ // truncation.
+ Val.trunc(NewWidth);
+ } else if (NewSign != Val.isSigned()) {
+ // Convert the sign to match the sign of the condition. This can cause
+ // overflow as well: unsigned(INTMIN)
+ llvm::APSInt OldVal(Val);
+ Val.setIsSigned(NewSign);
+
+ if (Val.isNegative()) // Sign bit changes meaning.
+ Diag(Loc, DiagID, OldVal.toString(), Val.toString());
+ }
+}
+
Action::StmtResult
Sema::FinishSwitchStmt(SourceLocation SwitchLoc, StmtTy *Switch, ExprTy *Body) {
Stmt *BodyStmt = (Stmt*)Body;
@@ -162,37 +208,88 @@
SS->setBody(BodyStmt);
SwitchStack.pop_back();
- Expr *condExpr = SS->getCond();
- QualType condType = condExpr->getType();
+ Expr *CondExpr = SS->getCond();
+ QualType CondType = CondExpr->getType();
- if (!condType->isIntegerType()) { // C99 6.8.4.2p1
+ if (!CondType->isIntegerType()) { // C99 6.8.4.2p1
Diag(SwitchLoc, diag::err_typecheck_statement_requires_integer,
- condType.getAsString(), condExpr->getSourceRange());
- return false;
+ CondType.getAsString(), CondExpr->getSourceRange());
+ return true;
}
-
- DefaultStmt *CurDefaultStmt = 0;
+
+ // Get the bitwidth of the switched-on value before promotions. We must
+ // convert the integer case values to this width before comparison.
+ unsigned CondWidth = Context.getTypeSize(CondType, SwitchLoc);
+ bool CondIsSigned = CondType->isSignedIntegerType();
+
+ // Accumulate all of the case values in a vector so that we can sort them
+ // and detect duplicates. This vector contains the APInt for the case after
+ // it has been converted to the condition type.
+ llvm::SmallVector<std::pair<llvm::APSInt, CaseStmt*>, 64> CaseVals;
+
+ // Keep track of any GNU case ranges we see. The APSInt is the low value.
+ std::vector<std::pair<llvm::APSInt, CaseStmt*> > CaseRanges;
+
+ DefaultStmt *TheDefaultStmt = 0;
// FIXME: The list of cases is backwards and needs to be reversed.
for (SwitchCase *SC = SS->getSwitchCaseList(); SC;
SC = SC->getNextSwitchCase()) {
if (DefaultStmt *DS = dyn_cast<DefaultStmt>(SC)) {
- if (CurDefaultStmt) {
- Diag(DS->getDefaultLoc(),
- diag::err_multiple_default_labels_defined);
- Diag(CurDefaultStmt->getDefaultLoc(),
- diag::err_first_label);
+ if (TheDefaultStmt) {
+ Diag(DS->getDefaultLoc(), diag::err_multiple_default_labels_defined);
+ Diag(TheDefaultStmt->getDefaultLoc(), diag::err_first_label);
- // FIXME: Remove the default statement from the switch block
- // so that we'll return a valid AST.
- } else {
- CurDefaultStmt = DS;
+ // FIXME: Remove the default statement from the switch block so that
+ // we'll return a valid AST. This requires recursing down the
+ // AST and finding it, not something we are set up to do right now. For
+ // now, just lop the entire switch stmt out of the AST.
+ return true;
}
+ TheDefaultStmt = DS;
- // FIXME: Check that case values are unique here.
- }
- }
+ } else {
+ CaseStmt *CS = cast<CaseStmt>(SC);
+
+ // We already verified that the expression has a i-c-e value (C99
+ // 6.8.4.2p3) - get that value now.
+ llvm::APSInt LoVal(32);
+ CS->getLHS()->isIntegerConstantExpr(LoVal, Context);
+
+ // Convert the value to the same width/sign as the condition.
+ ConvertIntegerToTypeWarnOnOverflow(LoVal, CondWidth, CondIsSigned,
+ CS->getLHS()->getLocStart(),
+ diag::warn_case_value_overflow);
+ // Remember the converted value.
+ CaseVals.push_back(std::make_pair(LoVal, CS));
+
+ // Just remember where the case range was.
+ if (CS->getRHS())
+ CaseRanges.push_back(std::make_pair(LoVal, CS));
+ }
+ }
+
+
+
+#if 0
+ assert(CaseRanges.empty() && "FIXME: Check case ranges for overlap etc");
+ // TODO: if the low value is bigger than the high value, the case is
+ // empty: emit "empty range specified" warning and drop the c
+
+ llvm::APSInt HiVal(32);
+ RHS->isIntegerConstantExpr(HiVal, Context);
+
+ // Convert the value to the same width/sign as the condition.
+ ConvertIntegerToTypeWarnOnOverflow(HiVal, CondWidth, CondIsSigned);
+
+ // If low value and high value equal, just forget about it as a range
+ // for the purposes of checking: it identifies a single value.
+ if (LoVal == HiVal)
+ continue;
+
+#endif
+
return SS;
}
Modified: cfe/trunk/include/clang/Basic/DiagnosticKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticKinds.def?rev=41313&r1=41312&r2=41313&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticKinds.def Thu Aug 23 00:46:52 2007
@@ -713,6 +713,8 @@
"'default' statement not in switch statement")
DIAG(err_case_not_in_switch, ERROR,
"'case' statement not in switch statement")
+DIAG(warn_case_value_overflow, WARNING,
+ "overflow converting case value to switch condition type (%0 to %1)")
DIAG(err_typecheck_return_incompatible, ERROR,
"incompatible type returning '%1', expected '%0'")
DIAG(ext_typecheck_return_pointer_int, WARNING,
Modified: cfe/trunk/test/Sema/switch.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/switch.c?rev=41313&r1=41312&r2=41313&view=diff
==============================================================================
--- cfe/trunk/test/Sema/switch.c (original)
+++ cfe/trunk/test/Sema/switch.c Thu Aug 23 00:46:52 2007
@@ -1,9 +1,16 @@
// RUN: clang -parse-ast-check %s
-
void f (int z) {
while (z) {
default: z--; // expected-error {{statement not in switch}}
}
}
+void foo(int X) {
+ switch (X) {
+ case 5000000000LL: // expected-warning {{overflow}}
+ case 42:
+ ;
+ }
+}
+
More information about the cfe-commits
mailing list