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. /// EvaluateAsLValue - Evaluate an expression to see if it's a lvalue.
bool EvaluateAsAnyLValue(EvalResult &Result, const ASTContext &Ctx) const; 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() /// \brief Enumeration used to describe how \c isNullPointerConstant()
/// should cope with value-dependent expressions. /// should cope with value-dependent expressions.
enum NullPointerConstantValueDependence { enum NullPointerConstantValueDependence {
@ -452,11 +468,12 @@ public:
NPC_ValueDependentIsNotNull NPC_ValueDependentIsNotNull
}; };
/// isNullPointerConstant - C99 6.3.2.3p3 - Return true if this is either an /// isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to
/// integer constant expression with the value zero, or if this is one that is /// a Null pointer constant. The return value can further distinguish the
/// cast to void*. /// kind of NULL pointer constant that was detected.
bool isNullPointerConstant(ASTContext &Ctx, NullPointerConstantKind isNullPointerConstant(
NullPointerConstantValueDependence NPC) const; ASTContext &Ctx,
NullPointerConstantValueDependence NPC) const;
/// isOBJCGCCandidate - Return true if this expression may be used in a read/ /// isOBJCGCCandidate - Return true if this expression may be used in a read/
/// write barrier. /// write barrier.

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

@ -3147,6 +3147,8 @@ def err_incomplete_type_used_in_type_trait_expr : Error<
"incomplete type %0 used in type trait expression">; "incomplete type %0 used in type trait expression">;
def err_expected_ident_or_lparen : Error<"expected identifier or '('">; 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. } // End of general sema category.
// inline asm. // inline asm.

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

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

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

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

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

@ -5229,6 +5229,46 @@ ExprResult Sema::ActOnParenOrParenListExpr(SourceLocation L,
return Owned(expr); 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. /// Note that lhs is not null here, even if this is the gnu "x ?: y" extension.
/// In that case, lhs = cond. /// In that case, lhs = cond.
/// C99 6.5.15 /// C99 6.5.15
@ -5471,6 +5511,12 @@ QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS,
return LHSTy; 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. // Otherwise, the operands are not compatible.
Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands)
<< LHSTy << RHSTy << LHS->getSourceRange() << RHS->getSourceRange(); << 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 /// value operand is a class type, overload resolution is used to find a
/// conversion to a common type. /// conversion to a common type.
static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS, static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS,
SourceLocation Loc) { SourceLocation QuestionLoc) {
Expr *Args[2] = { LHS, RHS }; Expr *Args[2] = { LHS, RHS };
OverloadCandidateSet CandidateSet(Loc); OverloadCandidateSet CandidateSet(QuestionLoc);
Self.AddBuiltinOperatorCandidates(OO_Conditional, Loc, Args, 2, CandidateSet); Self.AddBuiltinOperatorCandidates(OO_Conditional, QuestionLoc, Args, 2,
CandidateSet);
OverloadCandidateSet::iterator Best; OverloadCandidateSet::iterator Best;
switch (CandidateSet.BestViableFunction(Self, Loc, Best)) { switch (CandidateSet.BestViableFunction(Self, QuestionLoc, Best)) {
case OR_Success: case OR_Success:
// We found a match. Perform the conversions on the arguments and move on. // We found a match. Perform the conversions on the arguments and move on.
if (Self.PerformImplicitConversion(LHS, Best->BuiltinTypes.ParamTypes[0], if (Self.PerformImplicitConversion(LHS, Best->BuiltinTypes.ParamTypes[0],
@ -2891,13 +2892,20 @@ static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS,
return false; return false;
case OR_No_Viable_Function: 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->getType() << RHS->getType()
<< LHS->getSourceRange() << RHS->getSourceRange(); << LHS->getSourceRange() << RHS->getSourceRange();
return true; return true;
case OR_Ambiguous: case OR_Ambiguous:
Self.Diag(Loc, diag::err_conditional_ambiguous_ovl) Self.Diag(QuestionLoc, diag::err_conditional_ambiguous_ovl)
<< LHS->getType() << RHS->getType() << LHS->getType() << RHS->getType()
<< LHS->getSourceRange() << RHS->getSourceRange(); << LHS->getSourceRange() << RHS->getSourceRange();
// FIXME: Print the possible common types by printing the return types of // 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. // We can suppress this because the immediate context wants an int.
return (x != 0) ? 0U : x; 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. // Verify that null is evaluated as 0.
int b[__null ? -1 : 1]; 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)(1 + nullptr); // expected-error {{invalid operands to binary expression}}
(void)(0 ? nullptr : 0); // expected-error {{incompatible operand types}} (void)(0 ? nullptr : 0); // expected-error {{incompatible operand types}}
(void)(0 ? nullptr : (void*)0); (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 // Overloading
int t = o1(nullptr); int t = o1(nullptr);