From 091f23f1d6d4bcffd6641cda72a6831e08c02ea7 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 9 Nov 2010 22:22:12 +0000 Subject: [PATCH] Split out -Wconversion warnings about constant precision into their own subcategory, -Wconstant-conversion, which is on by default. Tweak the constant folder to give better results in the invalid case of a negative shift amount. Implements rdar://problem/6792488 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@118636 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticGroups.td | 11 ++++++-- include/clang/Basic/DiagnosticSemaKinds.td | 3 ++ lib/AST/ExprConstant.cpp | 19 +++++++++++-- lib/Sema/SemaChecking.cpp | 26 ++++++++++++++++++ test/Lexer/constants.c | 8 +++--- test/Preprocessor/pushable-diagnostics.c | 6 ++-- test/Sema/constant-conversion.c | 9 ++++++ test/Sema/conversion.c | 32 +++++++++++----------- 8 files changed, 85 insertions(+), 29 deletions(-) create mode 100644 test/Sema/constant-conversion.c diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 6ef7df6df9..c89ab09b3a 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -164,10 +164,15 @@ def CharSubscript : DiagGroup<"char-subscripts">; // missing parentheses; it is off by default. def Parentheses : DiagGroup<"parentheses", [DiagGroup<"idiomatic-parentheses">]>; -// -Wconversion has its own warnings, but we split this one out for -// legacy reasons. +// -Wconversion has its own warnings, but we split a few out for +// legacy reasons: +// - some people want just 64-to-32 warnings +// - conversion warnings with constant sources are on by default +// - bool-to-pointer conversion warnings are on by default def Conversion : DiagGroup<"conversion", - [DiagGroup<"shorten-64-to-32">, BoolConversions]>, + [DiagGroup<"shorten-64-to-32">, + DiagGroup<"constant-conversion">, + BoolConversions]>, DiagCategory<"Value Conversion Issue">; def Unused : DiagGroup<"unused", diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 1d025c81a3..7b17d7093e 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1041,6 +1041,9 @@ def warn_impcast_integer_precision : Warning< def warn_impcast_integer_64_32 : Warning< "implicit conversion loses integer precision: %0 to %1">, InGroup>, DefaultIgnore; +def warn_impcast_integer_precision_constant : Warning< + "implicit conversion from %2 to %3 changes value from %0 to %1">, + InGroup>; def warn_cast_align : Warning< "cast from %0 to %1 increases required alignment from %2 to %3">, diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index ced3839812..451aa2a253 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1458,12 +1458,25 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { return Error(E->getOperatorLoc(), diag::note_expr_divide_by_zero, E); return Success(Result.getInt() % RHS, E); case BO_Shl: { - // FIXME: Warn about out of range shift amounts! - unsigned SA = - (unsigned) RHS.getLimitedValue(Result.getInt().getBitWidth()-1); + // During constant-folding, a negative shift is an opposite shift. + if (RHS.isSigned() && RHS.isNegative()) { + RHS = -RHS; + goto shift_right; + } + + shift_left: + unsigned SA + = (unsigned) RHS.getLimitedValue(Result.getInt().getBitWidth()-1); return Success(Result.getInt() << SA, E); } case BO_Shr: { + // During constant-folding, a negative shift is an opposite shift. + if (RHS.isSigned() && RHS.isNegative()) { + RHS = -RHS; + goto shift_left; + } + + shift_right: unsigned SA = (unsigned) RHS.getLimitedValue(Result.getInt().getBitWidth()-1); return Success(Result.getInt() >> SA, E); diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index acd830ddec..4cf466311b 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -2165,8 +2165,12 @@ struct IntRange { if (const ComplexType *CT = dyn_cast(T)) T = CT->getElementType().getTypePtr(); + // For enum types, use the known bit width of the enumerators. if (const EnumType *ET = dyn_cast(T)) { EnumDecl *Enum = ET->getDecl(); + if (!Enum->isDefinition()) + return IntRange(C.getIntWidth(QualType(T, 0)), false); + unsigned NumPositive = Enum->getNumPositiveBits(); unsigned NumNegative = Enum->getNumNegativeBits(); @@ -2590,6 +2594,15 @@ void DiagnoseImpCast(Sema &S, Expr *E, QualType T, SourceLocation CContext, << E->getType() << T << E->getSourceRange() << SourceRange(CContext); } +std::string PrettyPrintInRange(const llvm::APSInt &Value, IntRange Range) { + if (!Range.Width) return "0"; + + llvm::APSInt ValueInRange = Value; + ValueInRange.setIsSigned(!Range.NonNegative); + ValueInRange.trunc(Range.Width); + return ValueInRange.toString(10); +} + void CheckImplicitConversion(Sema &S, Expr *E, QualType T, SourceLocation CC, bool *ICContext = 0) { if (E->isTypeDependent() || E->isValueDependent()) return; @@ -2670,6 +2683,19 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T, IntRange TargetRange = IntRange::forCanonicalType(S.Context, Target); if (SourceRange.Width > TargetRange.Width) { + // If the source is a constant, use a default-on diagnostic. + // TODO: this should happen for bitfield stores, too. + llvm::APSInt Value(32); + if (E->isIntegerConstantExpr(Value, S.Context)) { + std::string PrettySourceValue = Value.toString(10); + std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange); + + S.Diag(E->getExprLoc(), diag::warn_impcast_integer_precision_constant) + << PrettySourceValue << PrettyTargetValue + << E->getType() << T << E->getSourceRange() << clang::SourceRange(CC); + return; + } + // People want to build with -Wshorten-64-to-32 and not -Wconversion // and by god we'll let them. if (SourceRange.Width == 64 && TargetRange.Width == 32) diff --git a/test/Lexer/constants.c b/test/Lexer/constants.c index de0962e97d..3d2da2c764 100644 --- a/test/Lexer/constants.c +++ b/test/Lexer/constants.c @@ -15,7 +15,7 @@ float Y = 08.123456; #endif -char c[] = { +int c[] = { 'df', // expected-warning {{multi-character character constant}} '\t', '\\ @@ -34,12 +34,12 @@ int m3 = '\\\ #pragma clang diagnostic ignored "-Wmultichar" -char d = 'df'; // no warning. -char e = 'abcd'; // still warn: expected-warning {{multi-character character constant}} +int d = 'df'; // no warning. +int e = 'abcd'; // still warn: expected-warning {{multi-character character constant}} #pragma clang diagnostic ignored "-Wfour-char-constants" -char f = 'abcd'; // ignored. +int f = 'abcd'; // ignored. // rdar://problem/6974641 float t0[] = { diff --git a/test/Preprocessor/pushable-diagnostics.c b/test/Preprocessor/pushable-diagnostics.c index 567a866fa3..877eaaa0b3 100644 --- a/test/Preprocessor/pushable-diagnostics.c +++ b/test/Preprocessor/pushable-diagnostics.c @@ -4,14 +4,14 @@ #pragma clang diagnostic puhs // expected-warning {{pragma diagnostic expected 'error', 'warning', 'ignored', 'fatal', 'push', or 'pop'}} -char a = 'df'; // expected-warning{{multi-character character constant}} +int a = 'df'; // expected-warning{{multi-character character constant}} #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmultichar" -char b = 'df'; // no warning. +int b = 'df'; // no warning. #pragma clang diagnostic pop -char c = 'df'; // expected-warning{{multi-character character constant}} +int c = 'df'; // expected-warning{{multi-character character constant}} #pragma clang diagnostic pop // expected-warning{{pragma diagnostic pop could not pop, no matching push}} diff --git a/test/Sema/constant-conversion.c b/test/Sema/constant-conversion.c new file mode 100644 index 0000000000..cacd5968dc --- /dev/null +++ b/test/Sema/constant-conversion.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-apple-darwin %s + +// This file tests -Wconstant-conversion, a subcategory of -Wconversion +// which is on by default. + +// rdar://problem/6792488 +void test_6792488(void) { + int x = 0x3ff0000000000000U; // expected-warning {{implicit conversion from 'unsigned long' to 'int' changes value from 4607182418800017408 to 0}} +} diff --git a/test/Sema/conversion.c b/test/Sema/conversion.c index 411ca32b5a..e78902332e 100644 --- a/test/Sema/conversion.c +++ b/test/Sema/conversion.c @@ -40,17 +40,17 @@ void test0(char c, short s, int i, long l, long long ll) { l = (long) 0; c = (char) BIG; - c = (short) BIG; // expected-warning {{implicit conversion loses integer precision}} - c = (int) BIG; // expected-warning {{implicit conversion loses integer precision}} - c = (long) BIG; // expected-warning {{implicit conversion loses integer precision}} + c = (short) BIG; // expected-warning {{implicit conversion from 'short' to 'char' changes value}} + c = (int) BIG; // expected-warning {{implicit conversion from 'int' to 'char' changes value}} + c = (long) BIG; // expected-warning {{implicit conversion from 'long' to 'char' changes value}} s = (char) BIG; s = (short) BIG; - s = (int) BIG; // expected-warning {{implicit conversion loses integer precision}} - s = (long) BIG; // expected-warning {{implicit conversion loses integer precision}} + s = (int) BIG; // expected-warning {{implicit conversion from 'int' to 'short' changes value}} + s = (long) BIG; // expected-warning {{implicit conversion from 'long' to 'short' changes value}} i = (char) BIG; i = (short) BIG; i = (int) BIG; - i = (long) BIG; // expected-warning {{implicit conversion loses integer precision}} + i = (long) BIG; // expected-warning {{implicit conversion from 'long' to 'int' changes value}} l = (char) BIG; l = (short) BIG; l = (int) BIG; @@ -63,10 +63,10 @@ char test1(long long ll) { return (int) ll; // expected-warning {{implicit conversion loses integer precision}} return (short) ll; // expected-warning {{implicit conversion loses integer precision}} return (char) ll; - return (long long) BIG; // expected-warning {{implicit conversion loses integer precision}} - return (long) BIG; // expected-warning {{implicit conversion loses integer precision}} - return (int) BIG; // expected-warning {{implicit conversion loses integer precision}} - return (short) BIG; // expected-warning {{implicit conversion loses integer precision}} + return (long long) BIG; // expected-warning {{implicit conversion from 'long long' to 'char' changes value}} + return (long) BIG; // expected-warning {{implicit conversion from 'long' to 'char' changes value}} + return (int) BIG; // expected-warning {{implicit conversion from 'int' to 'char' changes value}} + return (short) BIG; // expected-warning {{implicit conversion from 'short' to 'char' changes value}} return (char) BIG; } @@ -76,9 +76,9 @@ short test2(long long ll) { return (int) ll; // expected-warning {{implicit conversion loses integer precision}} return (short) ll; return (char) ll; - return (long long) BIG; // expected-warning {{implicit conversion loses integer precision}} - return (long) BIG; // expected-warning {{implicit conversion loses integer precision}} - return (int) BIG; // expected-warning {{implicit conversion loses integer precision}} + return (long long) BIG; // expected-warning {{implicit conversion from 'long long' to 'short' changes value}} + return (long) BIG; // expected-warning {{implicit conversion from 'long' to 'short' changes value}} + return (int) BIG; // expected-warning {{implicit conversion from 'int' to 'short' changes value}} return (short) BIG; return (char) BIG; } @@ -89,8 +89,8 @@ int test3(long long ll) { return (int) ll; return (short) ll; return (char) ll; - return (long long) BIG; // expected-warning {{implicit conversion loses integer precision}} - return (long) BIG; // expected-warning {{implicit conversion loses integer precision}} + return (long long) BIG; // expected-warning {{implicit conversion from 'long long' to 'int' changes value}} + return (long) BIG; // expected-warning {{implicit conversion from 'long' to 'int' changes value}} return (int) BIG; return (short) BIG; return (char) BIG; @@ -277,7 +277,7 @@ unsigned char test19(unsigned long u64) { // void test_7631400(void) { // This should show up despite the caret being inside a macro substitution - char s = LONG_MAX; // expected-warning {{implicit conversion loses integer precision: 'long' to 'char'}} + char s = LONG_MAX; // expected-warning {{implicit conversion from 'long' to 'char' changes value}} } // : assertion for compound operators with non-integral RHS -- 2.40.0