Initial steps to improve diagnostics when there is a NULL and

a non-pointer on the two sides of a conditional expression.

Patch by Stephen Hines and Mihai Rusu.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@125995 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Chandler Carruth 2011-02-18 23:54:50 +00:00
Родитель 0656e5b9aa
Коммит 82214a80c0
10 изменённых файлов: 140 добавлений и 22 удалений

Просмотреть файл

@ -437,6 +437,22 @@ public:
/// EvaluateAsLValue - Evaluate an expression to see if it's a lvalue.
bool EvaluateAsAnyLValue(EvalResult &Result, const ASTContext &Ctx) const;
/// \brief Enumeration used to describe the kind of Null pointer constant
/// returned from \c isNullPointerConstant().
enum NullPointerConstantKind {
/// \brief Expression is not a Null pointer constant.
NPCK_NotNull = 0,
/// \brief Expression is a Null pointer constant built from a zero integer.
NPCK_ZeroInteger,
/// \brief Expression is a C++0X nullptr.
NPCK_CXX0X_nullptr,
/// \brief Expression is a GNU-style __null constant.
NPCK_GNUNull
};
/// \brief Enumeration used to describe how \c isNullPointerConstant()
/// should cope with value-dependent expressions.
enum NullPointerConstantValueDependence {
@ -452,11 +468,12 @@ public:
NPC_ValueDependentIsNotNull
};
/// isNullPointerConstant - C99 6.3.2.3p3 - Return true if this is either an
/// integer constant expression with the value zero, or if this is one that is
/// cast to void*.
bool isNullPointerConstant(ASTContext &Ctx,
NullPointerConstantValueDependence NPC) const;
/// isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to
/// a Null pointer constant. The return value can further distinguish the
/// kind of NULL pointer constant that was detected.
NullPointerConstantKind isNullPointerConstant(
ASTContext &Ctx,
NullPointerConstantValueDependence NPC) const;
/// isOBJCGCCandidate - Return true if this expression may be used in a read/
/// write barrier.

Просмотреть файл

@ -3147,6 +3147,8 @@ def err_incomplete_type_used_in_type_trait_expr : Error<
"incomplete type %0 used in type trait expression">;
def err_expected_ident_or_lparen : Error<"expected identifier or '('">;
def err_typecheck_cond_incompatible_operands_null : Error<
"non-pointer operand type %0 incompatible with %select{NULL|nullptr}1">;
} // End of general sema category.
// inline asm.

Просмотреть файл

@ -4774,6 +4774,9 @@ public:
QualType FindCompositeObjCPointerType(Expr *&LHS, Expr *&RHS,
SourceLocation questionLoc);
bool DiagnoseConditionalForNull(Expr *LHS, Expr *RHS,
SourceLocation QuestionLoc);
/// type checking for vector binary operators.
QualType CheckVectorOperands(SourceLocation l, Expr *&lex, Expr *&rex);
QualType CheckVectorCompareOperands(Expr *&lex, Expr *&rx,

Просмотреть файл

@ -2132,11 +2132,14 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const {
return isEvaluatable(Ctx);
}
/// isNullPointerConstant - C99 6.3.2.3p3 - Return true if this is either an
/// integer constant expression with the value zero, or if this is one that is
/// cast to void*.
bool Expr::isNullPointerConstant(ASTContext &Ctx,
NullPointerConstantValueDependence NPC) const {
/// isNullPointerConstant - C99 6.3.2.3p3 - Return whether this is a null
/// pointer constant or not, as well as the specific kind of constant detected.
/// Null pointer constants can be integer constant expressions with the
/// value zero, casts of zero to void*, nullptr (C++0X), or __null
/// (a GNU extension).
Expr::NullPointerConstantKind
Expr::isNullPointerConstant(ASTContext &Ctx,
NullPointerConstantValueDependence NPC) const {
if (isValueDependent()) {
switch (NPC) {
case NPC_NeverValueDependent:
@ -2144,10 +2147,13 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx,
// If the unthinkable happens, fall through to the safest alternative.
case NPC_ValueDependentIsNull:
return isTypeDependent() || getType()->isIntegralType(Ctx);
if (isTypeDependent() || getType()->isIntegralType(Ctx))
return NPCK_ZeroInteger;
else
return NPCK_NotNull;
case NPC_ValueDependentIsNotNull:
return false;
return NPCK_NotNull;
}
}
@ -2176,12 +2182,12 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx,
return DefaultArg->getExpr()->isNullPointerConstant(Ctx, NPC);
} else if (isa<GNUNullExpr>(this)) {
// The GNU __null extension is always a null pointer constant.
return true;
return NPCK_GNUNull;
}
// C++0x nullptr_t is always a null pointer constant.
if (getType()->isNullPtrType())
return true;
return NPCK_CXX0X_nullptr;
if (const RecordType *UT = getType()->getAsUnionType())
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>())
@ -2193,12 +2199,14 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx,
// This expression must be an integer type.
if (!getType()->isIntegerType() ||
(Ctx.getLangOptions().CPlusPlus && getType()->isEnumeralType()))
return false;
return NPCK_NotNull;
// If we have an integer constant expression, we need to *evaluate* it and
// test for the value 0.
llvm::APSInt Result;
return isIntegerConstantExpr(Result, Ctx) && Result == 0;
bool IsNull = isIntegerConstantExpr(Result, Ctx) && Result == 0;
return (IsNull ? NPCK_ZeroInteger : NPCK_NotNull);
}
/// \brief If this expression is an l-value for an Objective C

Просмотреть файл

@ -5229,6 +5229,46 @@ ExprResult Sema::ActOnParenOrParenListExpr(SourceLocation L,
return Owned(expr);
}
/// \brief Emit a specialized diagnostic when one expression is a null pointer
/// constant and the other is not a pointer.
bool Sema::DiagnoseConditionalForNull(Expr *LHS, Expr *RHS,
SourceLocation QuestionLoc) {
Expr *NullExpr = LHS;
Expr *NonPointerExpr = RHS;
Expr::NullPointerConstantKind NullKind =
NullExpr->isNullPointerConstant(Context,
Expr::NPC_ValueDependentIsNotNull);
if (NullKind == Expr::NPCK_NotNull) {
NullExpr = RHS;
NonPointerExpr = LHS;
NullKind =
NullExpr->isNullPointerConstant(Context,
Expr::NPC_ValueDependentIsNotNull);
}
if (NullKind == Expr::NPCK_NotNull)
return false;
if (NullKind == Expr::NPCK_ZeroInteger) {
// In this case, check to make sure that we got here from a "NULL"
// string in the source code.
NullExpr = NullExpr->IgnoreParenImpCasts();
SourceManager& SM = Context.getSourceManager();
SourceLocation Loc = SM.getInstantiationLoc(NullExpr->getExprLoc());
unsigned Len =
Lexer::MeasureTokenLength(Loc, SM, Context.getLangOptions());
if (Len != 4 || memcmp(SM.getCharacterData(Loc), "NULL", 4))
return false;
}
int DiagType = (NullKind == Expr::NPCK_CXX0X_nullptr);
Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands_null)
<< NonPointerExpr->getType() << DiagType
<< NonPointerExpr->getSourceRange();
return true;
}
/// Note that lhs is not null here, even if this is the gnu "x ?: y" extension.
/// In that case, lhs = cond.
/// C99 6.5.15
@ -5471,6 +5511,12 @@ QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS,
return LHSTy;
}
// Emit a better diagnostic if one of the expressions is a null pointer
// constant and the other is not a pointer type. In this case, the user most
// likely forgot to take the address of the other expression.
if (DiagnoseConditionalForNull(LHS, RHS, QuestionLoc))
return QualType();
// Otherwise, the operands are not compatible.
Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands)
<< LHSTy << RHSTy << LHS->getSourceRange() << RHS->getSourceRange();

Просмотреть файл

@ -2874,13 +2874,14 @@ static bool TryClassUnification(Sema &Self, Expr *From, Expr *To,
/// value operand is a class type, overload resolution is used to find a
/// conversion to a common type.
static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS,
SourceLocation Loc) {
SourceLocation QuestionLoc) {
Expr *Args[2] = { LHS, RHS };
OverloadCandidateSet CandidateSet(Loc);
Self.AddBuiltinOperatorCandidates(OO_Conditional, Loc, Args, 2, CandidateSet);
OverloadCandidateSet CandidateSet(QuestionLoc);
Self.AddBuiltinOperatorCandidates(OO_Conditional, QuestionLoc, Args, 2,
CandidateSet);
OverloadCandidateSet::iterator Best;
switch (CandidateSet.BestViableFunction(Self, Loc, Best)) {
switch (CandidateSet.BestViableFunction(Self, QuestionLoc, Best)) {
case OR_Success:
// We found a match. Perform the conversions on the arguments and move on.
if (Self.PerformImplicitConversion(LHS, Best->BuiltinTypes.ParamTypes[0],
@ -2891,13 +2892,20 @@ static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS,
return false;
case OR_No_Viable_Function:
Self.Diag(Loc, diag::err_typecheck_cond_incompatible_operands)
// Emit a better diagnostic if one of the expressions is a null pointer
// constant and the other is a pointer type. In this case, the user most
// likely forgot to take the address of the other expression.
if (Self.DiagnoseConditionalForNull(LHS, RHS, QuestionLoc))
return true;
Self.Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands)
<< LHS->getType() << RHS->getType()
<< LHS->getSourceRange() << RHS->getSourceRange();
return true;
case OR_Ambiguous:
Self.Diag(Loc, diag::err_conditional_ambiguous_ovl)
Self.Diag(QuestionLoc, diag::err_conditional_ambiguous_ovl)
<< LHS->getType() << RHS->getType()
<< LHS->getSourceRange() << RHS->getSourceRange();
// FIXME: Print the possible common types by printing the return types of

Просмотреть файл

@ -75,3 +75,16 @@ int f2(int x) {
// We can suppress this because the immediate context wants an int.
return (x != 0) ? 0U : x;
}
#define NULL (void*)0
void PR9236() {
struct A {int i;} A1;
(void)(1 ? A1 : NULL); // expected-error{{non-pointer operand type 'struct A' incompatible with NULL}}
(void)(1 ? NULL : A1); // expected-error{{non-pointer operand type 'struct A' incompatible with NULL}}
(void)(1 ? 0 : A1); // expected-error{{incompatible operand types}}
(void)(1 ? (void*)0 : A1); // expected-error{{incompatible operand types}}
(void)(1 ? A1: (void*)0); // expected-error{{incompatible operand types}}
(void)(1 ? A1 : (NULL)); // expected-error{{non-pointer operand type 'struct A' incompatible with NULL}}
}

Просмотреть файл

@ -12,3 +12,10 @@ void f() {
// Verify that null is evaluated as 0.
int b[__null ? -1 : 1];
}
struct A {};
void g() {
(void)(0 ? __null : A()); // expected-error {{non-pointer operand type 'A' incompatible with NULL}}
(void)(0 ? A(): __null); // expected-error {{non-pointer operand type 'A' incompatible with NULL}}
}

Просмотреть файл

@ -307,3 +307,15 @@ namespace PR7598 {
}
}
namespace PR9236 {
#define NULL 0L
void f() {
(void)(true ? A() : NULL); // expected-error{{non-pointer operand type 'A' incompatible with NULL}}
(void)(true ? NULL : A()); // expected-error{{non-pointer operand type 'A' incompatible with NULL}}
(void)(true ? 0 : A()); // expected-error{{incompatible operand types}}
(void)(true ? nullptr : A()); // expected-error{{non-pointer operand type 'A' incompatible with nullptr}}
(void)(true ? __null : A()); // expected-error{{non-pointer operand type 'A' incompatible with NULL}}
(void)(true ? (void*)0 : A()); // expected-error{{incompatible operand types}}
}
}

Просмотреть файл

@ -46,6 +46,8 @@ nullptr_t f(nullptr_t null)
(void)(1 + nullptr); // expected-error {{invalid operands to binary expression}}
(void)(0 ? nullptr : 0); // expected-error {{incompatible operand types}}
(void)(0 ? nullptr : (void*)0);
(void)(0 ? nullptr : A()); // expected-error {{non-pointer operand type 'A' incompatible with nullptr}}
(void)(0 ? A() : nullptr); // expected-error {{non-pointer operand type 'A' incompatible with nullptr}}
// Overloading
int t = o1(nullptr);