Extend the bitfield-truncation warning to initializations.

rdar://problem/8652606

llvm-svn: 118773
This commit is contained in:
John McCall 2010-11-11 03:21:53 +00:00
parent 6ed689bfcb
commit 1f425648ca
5 changed files with 92 additions and 39 deletions

View File

@ -4539,6 +4539,9 @@ private:
void CheckFloatComparison(SourceLocation loc, Expr* lex, Expr* rex); void CheckFloatComparison(SourceLocation loc, Expr* lex, Expr* rex);
void CheckImplicitConversions(Expr *E, SourceLocation CC = SourceLocation()); void CheckImplicitConversions(Expr *E, SourceLocation CC = SourceLocation());
void CheckBitFieldInitialization(SourceLocation InitLoc, FieldDecl *Field,
Expr *Init);
/// \brief The parser's current scope. /// \brief The parser's current scope.
/// ///
/// The parser maintains this state here. /// The parser maintains this state here.
@ -4546,6 +4549,7 @@ private:
protected: protected:
friend class Parser; friend class Parser;
friend class InitializationSequence;
/// \brief Retrieve the parser's current scope. /// \brief Retrieve the parser's current scope.
Scope *getCurScope() const { return CurScope; } Scope *getCurScope() const { return CurScope; }

View File

@ -2608,6 +2608,55 @@ void AnalyzeComparison(Sema &S, BinaryOperator *E) {
<< lex->getSourceRange() << rex->getSourceRange(); << lex->getSourceRange() << rex->getSourceRange();
} }
/// Analyzes an attempt to assign the given value to a bitfield.
///
/// Returns true if there was something fishy about the attempt.
bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
SourceLocation InitLoc) {
assert(Bitfield->isBitField());
if (Bitfield->isInvalidDecl())
return false;
Expr *OriginalInit = Init->IgnoreParenImpCasts();
llvm::APSInt Width(32);
Expr::EvalResult InitValue;
if (!Bitfield->getBitWidth()->isIntegerConstantExpr(Width, S.Context) ||
!Init->Evaluate(InitValue, S.Context) ||
!InitValue.Val.isInt())
return false;
const llvm::APSInt &Value = InitValue.Val.getInt();
unsigned OriginalWidth = Value.getBitWidth();
unsigned FieldWidth = Width.getZExtValue();
if (OriginalWidth <= FieldWidth)
return false;
llvm::APSInt TruncatedValue = Value;
TruncatedValue.trunc(FieldWidth);
// It's fairly common to write values into signed bitfields
// that, if sign-extended, would end up becoming a different
// value. We don't want to warn about that.
if (Value.isSigned() && Value.isNegative())
TruncatedValue.sext(OriginalWidth);
else
TruncatedValue.zext(OriginalWidth);
if (Value == TruncatedValue)
return false;
std::string PrettyValue = Value.toString(10);
std::string PrettyTrunc = TruncatedValue.toString(10);
S.Diag(InitLoc, diag::warn_impcast_bitfield_precision_constant)
<< PrettyValue << PrettyTrunc << OriginalInit->getType()
<< Init->getSourceRange();
return true;
}
/// Analyze the given simple or compound assignment for warning-worthy /// Analyze the given simple or compound assignment for warning-worthy
/// operations. /// operations.
void AnalyzeAssignment(Sema &S, BinaryOperator *E) { void AnalyzeAssignment(Sema &S, BinaryOperator *E) {
@ -2617,44 +2666,11 @@ void AnalyzeAssignment(Sema &S, BinaryOperator *E) {
// We want to recurse on the RHS as normal unless we're assigning to // We want to recurse on the RHS as normal unless we're assigning to
// a bitfield. // a bitfield.
if (FieldDecl *Bitfield = E->getLHS()->getBitField()) { if (FieldDecl *Bitfield = E->getLHS()->getBitField()) {
assert(Bitfield->isBitField()); if (AnalyzeBitFieldAssignment(S, Bitfield, E->getRHS(),
E->getOperatorLoc())) {
Expr *RHS = E->getRHS()->IgnoreParenImpCasts(); // Recurse, ignoring any implicit conversions on the RHS.
return AnalyzeImplicitConversions(S, E->getRHS()->IgnoreParenImpCasts(),
llvm::APSInt Width(32); E->getOperatorLoc());
Expr::EvalResult RHSValue;
if (!Bitfield->isInvalidDecl() &&
Bitfield->getBitWidth()->isIntegerConstantExpr(Width, S.Context) &&
RHS->Evaluate(RHSValue, S.Context) && RHSValue.Val.isInt()) {
const llvm::APSInt &Value = RHSValue.Val.getInt();
unsigned OriginalWidth = Value.getBitWidth();
unsigned FieldWidth = Width.getZExtValue();
if (OriginalWidth > FieldWidth) {
llvm::APSInt TruncatedValue = Value;
TruncatedValue.trunc(FieldWidth);
// It's fairly common to write values into signed bitfields
// that, if sign-extended, would end up becoming a different
// value. We don't want to warn about that.
if (Value.isSigned() && Value.isNegative())
TruncatedValue.sext(OriginalWidth);
else
TruncatedValue.zext(OriginalWidth);
if (Value != TruncatedValue) {
std::string PrettyValue = Value.toString(10);
std::string PrettyTrunc = TruncatedValue.toString(10);
S.Diag(E->getOperatorLoc(),
diag::warn_impcast_bitfield_precision_constant)
<< PrettyValue << PrettyTrunc << RHS->getType()
<< E->getRHS()->getSourceRange();
// Recurse, ignoring any implicit conversions on the RHS.
return AnalyzeImplicitConversions(S, RHS, E->getOperatorLoc());
}
}
} }
} }
@ -2932,6 +2948,12 @@ void Sema::CheckImplicitConversions(Expr *E, SourceLocation CC) {
AnalyzeImplicitConversions(*this, E, CC); AnalyzeImplicitConversions(*this, E, CC);
} }
void Sema::CheckBitFieldInitialization(SourceLocation InitLoc,
FieldDecl *BitField,
Expr *Init) {
(void) AnalyzeBitFieldAssignment(*this, BitField, Init, InitLoc);
}
/// CheckParmsForFunctionDef - Check that the parameters of the given /// CheckParmsForFunctionDef - Check that the parameters of the given
/// function are appropriate for the definition of a function. This /// function are appropriate for the definition of a function. This
/// takes care of any checks that cannot be performed on the /// takes care of any checks that cannot be performed on the

View File

@ -4047,6 +4047,13 @@ InitializationSequence::Perform(Sema &S,
break; break;
} }
} }
// Diagnose non-fatal problems with the completed initialization.
if (Entity.getKind() == InitializedEntity::EK_Member &&
cast<FieldDecl>(Entity.getDecl())->isBitField())
S.CheckBitFieldInitialization(Kind.getLocation(),
cast<FieldDecl>(Entity.getDecl()),
CurInit.get());
return move(CurInit); return move(CurInit);
} }
@ -4534,7 +4541,7 @@ Sema::PerformCopyInitialization(const InitializedEntity &Entity,
if (Init.isInvalid()) if (Init.isInvalid())
return ExprError(); return ExprError();
Expr *InitE = (Expr *)Init.get(); Expr *InitE = Init.get();
assert(InitE && "No initialization expression?"); assert(InitE && "No initialization expression?");
if (EqualLoc.isInvalid()) if (EqualLoc.isInvalid())

View File

@ -24,3 +24,16 @@ enum Test2 test2(enum Test2 *t) {
*t = 20; *t = 20;
return 10; // shouldn't warn return 10; // shouldn't warn
} }
void test3() {
struct A {
unsigned int foo : 2;
int bar : 2;
};
struct A a = { 0, 10 }; // expected-warning {{implicit truncation from 'int' to bitfield changes value from 10 to 2}}
struct A b[] = { 0, 10, 0, 0 }; // expected-warning {{implicit truncation from 'int' to bitfield changes value from 10 to 2}}
struct A c[] = {{10, 0}}; // expected-warning {{implicit truncation from 'int' to bitfield changes value from 10 to 2}}
struct A d = (struct A) { 10, 0 }; // expected-warning {{implicit truncation from 'int' to bitfield changes value from 10 to 2}}
struct A e = { .foo = 10 }; // expected-warning {{implicit truncation from 'int' to bitfield changes value from 10 to 2}}
}

View File

@ -43,3 +43,10 @@ namespace test1 {
return p == foo(); return p == foo();
} }
} }
namespace test2 {
struct A {
unsigned int x : 2;
A() : x(10) {} // expected-warning {{implicit truncation from 'int' to bitfield changes value from 10 to 2}}
};
}