diff options
| author | Chandler Carruth <chandlerc@gmail.com> | 2011-02-18 23:54:50 +0000 | 
|---|---|---|
| committer | Chandler Carruth <chandlerc@gmail.com> | 2011-02-18 23:54:50 +0000 | 
| commit | a8bea4b90ec7448231306920b7a55ab4a13841cc (patch) | |
| tree | 38d943d1ad2cd6ca939aae044d4dc01ad74544a5 | |
| parent | 55532be31fbc60797efe9a181b01f0a4f8058fa0 (diff) | |
| download | bcm5719-llvm-a8bea4b90ec7448231306920b7a55ab4a13841cc.tar.gz bcm5719-llvm-a8bea4b90ec7448231306920b7a55ab4a13841cc.zip  | |
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.
llvm-svn: 125995
| -rw-r--r-- | clang/include/clang/AST/Expr.h | 27 | ||||
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 | ||||
| -rw-r--r-- | clang/include/clang/Sema/Sema.h | 3 | ||||
| -rw-r--r-- | clang/lib/AST/Expr.cpp | 30 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 46 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 20 | ||||
| -rw-r--r-- | clang/test/Sema/conditional-expr.c | 13 | ||||
| -rw-r--r-- | clang/test/SemaCXX/__null.cpp | 7 | ||||
| -rw-r--r-- | clang/test/SemaCXX/conditional-expr.cpp | 12 | ||||
| -rw-r--r-- | clang/test/SemaCXX/nullptr.cpp | 2 | 
10 files changed, 140 insertions, 22 deletions
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 36644784c90..a17205c2b6b 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -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. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7b28463349c..9592cbb36ff 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 8d8cf099129..4e3173ab09c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 7e46d4fe450..391b26ab48e 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -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 diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 7e6eee7fb7b..792eb8af98b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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(); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 44508540d03..b78e52303e1 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -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 diff --git a/clang/test/Sema/conditional-expr.c b/clang/test/Sema/conditional-expr.c index 6e248bc3cfe..7a8c9e9f361 100644 --- a/clang/test/Sema/conditional-expr.c +++ b/clang/test/Sema/conditional-expr.c @@ -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}} +} + diff --git a/clang/test/SemaCXX/__null.cpp b/clang/test/SemaCXX/__null.cpp index 3583655134a..1989a45fb5f 100644 --- a/clang/test/SemaCXX/__null.cpp +++ b/clang/test/SemaCXX/__null.cpp @@ -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}} +} diff --git a/clang/test/SemaCXX/conditional-expr.cpp b/clang/test/SemaCXX/conditional-expr.cpp index 8ac0a9736ac..3881765cff3 100644 --- a/clang/test/SemaCXX/conditional-expr.cpp +++ b/clang/test/SemaCXX/conditional-expr.cpp @@ -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}} +  } +} diff --git a/clang/test/SemaCXX/nullptr.cpp b/clang/test/SemaCXX/nullptr.cpp index 666701c3c6e..7385fd42ad3 100644 --- a/clang/test/SemaCXX/nullptr.cpp +++ b/clang/test/SemaCXX/nullptr.cpp @@ -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);  | 

