Hi Ted,

Yes, we do. The real purpose of the nullptr is the last part of the test 
program where it allows you to distinguish between two overloaded 
functions, i.e.:

void func( int );
void func( int *);

If you call it with the standard NULL, it will call the "int" version, 
whereas if you call it with nullptr, it will call the "int *" version. 
It doesn't add that much and I was going to refactor the code a bit if 
it was approved to just have a single function isNullPtr() that would 
check for ConcreteInt 0 or NullPtrVal.

  - jim


On 4/20/2011 11:39 AM, Ted Kremenek wrote:
> Hi Jim,
>
> I'm not an expert on nullptr, but couldn't we just represent it with an SVal 
> that was a loc::ConcreteInt with a value of 0?  Do we need to add a new SVal?
>
> Ted
>
> On Apr 19, 2011, at 9:13 PM, Jim Goodnow II wrote:
>
>> Ok, here's a stab at implementing support for the C++ nullptr in the static 
>> analyzer. There's also a test file. Please review and give any feedback.
>>
>> - jim
>>
>> Index: include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
>> ===================================================================
>> --- include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h    
>> (revision 129845)
>> +++ include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h    
>> (working copy)
>> @@ -150,6 +150,10 @@
>>    DefinedSVal getBlockPointer(const BlockDecl *block, CanQualType locTy,
>>                                const LocationContext *locContext);
>>
>> +  DefinedSVal makeNullPtrVal( void ) {
>> +    return nonloc::NullPtrVal();
>> +  }
>> +
>>    NonLoc makeCompoundVal(QualType type, llvm::ImmutableList<SVal>  vals) {
>>      return nonloc::CompoundVal(BasicVals.getCompoundValData(type, vals));
>>    }
>> Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
>> ===================================================================
>> --- include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h    (revision 
>> 129845)
>> +++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h    (working copy)
>> @@ -276,7 +276,8 @@
>> namespace nonloc {
>>
>> enum Kind { ConcreteIntKind, SymbolValKind, SymExprValKind,
>> -            LocAsIntegerKind, CompoundValKind, LazyCompoundValKind };
>> +            LocAsIntegerKind, CompoundValKind, LazyCompoundValKind,
>> +            NullPtrValKind };
>>
>> class SymbolVal : public NonLoc {
>> public:
>> @@ -420,6 +421,27 @@
>>    }
>> };
>>
>> +// Specail SVal class for C0xx nullptr.
>> +class NullPtrVal : public NonLoc {
>> +  friend class ento::SValBuilder;
>> +
>> +  explicit NullPtrVal( void) :
>> +    NonLoc(NullPtrValKind, NULL) {
>> +    }
>> +
>> +public:
>> +
>> +  // Implement isa<T>  support.
>> +  static inline bool classof(const SVal* V) {
>> +    return V->getBaseKind() == NonLocKind&&
>> +           V->getSubKind() == NullPtrValKind;
>> +  }
>> +
>> +  static inline bool classof(const Loc* V) {
>> +    return V->getSubKind() == NullPtrValKind;
>> +  }
>> +};
>> +
>> } // end namespace ento::nonloc
>>
>> //==------------------------------------------------------------------------==//
>> Index: lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
>> ===================================================================
>> --- lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp    (revision 129845)
>> +++ lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp    (working copy)
>> @@ -85,7 +85,7 @@
>>    DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l);
>>
>>    // Check for null dereferences.
>> -  if (!isa<Loc>(location))
>> +  if (!isa<Loc>(location)&&  !isa<nonloc::NullPtrVal>(location))
>>      return;
>>
>>    const Stmt *S = C.getStmt();
>> Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
>> ===================================================================
>> --- lib/StaticAnalyzer/Core/ExprEngine.cpp    (revision 129845)
>> +++ lib/StaticAnalyzer/Core/ExprEngine.cpp    (working copy)
>> @@ -424,7 +424,6 @@
>>      case Stmt::CXXCatchStmtClass:
>>      case Stmt::CXXDependentScopeMemberExprClass:
>>      case Stmt::CXXForRangeStmtClass:
>> -    case Stmt::CXXNullPtrLiteralExprClass:
>>      case Stmt::CXXPseudoDestructorExprClass:
>>      case Stmt::CXXTemporaryObjectExprClass:
>>      case Stmt::CXXThrowExprClass:
>> @@ -523,6 +522,7 @@
>>      case Stmt::ExprWithCleanupsClass:
>>      case Stmt::FloatingLiteralClass:
>>      case Stmt::SizeOfPackExprClass:
>> +    case Stmt::CXXNullPtrLiteralExprClass:
>>        Dst.Add(Pred); // No-op. Simply propagate the current state unchanged.
>>        break;
>>
>> Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
>> ===================================================================
>> --- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp    (revision 129845)
>> +++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp    (working copy)
>> @@ -89,6 +89,10 @@
>>      return UnknownVal();
>>    }
>>
>> +  // Special C0xx nullptr case, just return it.
>> +  if (isa<nonloc::NullPtrVal>(val))
>> +    return val;
>> +
>>    if (!isa<nonloc::ConcreteInt>(val))
>>      return UnknownVal();
>>
>> @@ -293,6 +297,16 @@
>>          return evalCastFromNonLoc(lhs, resultTy);
>>      }
>>
>> +  // For nullptr, assume that only exact comparison is legal
>> +  // for which we know the result for all cases, otherwise,
>> +  // the result is unknown and should be caught by a checker.
>> +  if (isa<nonloc::NullPtrVal>(rhs)) {
>> +    if (op == BO_EQ)
>> +      return makeTruthVal(false, resultTy);
>> +    if (op == BO_NE)
>> +      return makeTruthVal(true, resultTy);
>> +    return UnknownVal();
>> +  }
>>    while (1) {
>>      switch (lhs.getSubKind()) {
>>      default:
>> @@ -803,6 +817,7 @@
>>      // If we get here, we have no way of comparing the regions.
>>      return UnknownVal();
>>    }
>> +
>>    }
>> }
>>
>> @@ -831,8 +846,20 @@
>>          return evalBinOpLL(state, op, lhs, loc::ConcreteInt(*x), resultTy);
>>        }
>>      }
>> +    if (isa<nonloc::NullPtrVal>(rhs)) {
>> +      // We know for sure that the two fields are not the same, since lhs
>> +      // is a Loc.
>> +      if (op == BO_EQ)
>> +        return makeTruthVal(false, resultTy);
>> +      if (op == BO_NE)
>> +        return makeTruthVal(true, resultTy);
>> +    }
>>    }
>>
>> +  // any other op with nullptr is bad
>> +  if (isa<nonloc::NullPtrVal>(rhs))
>> +    return UnknownVal();
>> +
>>    // We are dealing with pointer arithmetic.
>>
>>    // Handle pointer arithmetic on constant values.
>> Index: lib/StaticAnalyzer/Core/SVals.cpp
>> ===================================================================
>> --- lib/StaticAnalyzer/Core/SVals.cpp    (revision 129845)
>> +++ lib/StaticAnalyzer/Core/SVals.cpp    (working copy)
>> @@ -338,6 +338,9 @@
>> <<  '}';
>>        break;
>>      }
>> +    case nonloc::NullPtrValKind:
>> +      os<<  "NullPtrVal";
>> +      break;
>>      default:
>>        assert (false&&  "Pretty-printed not implemented for this NonLoc.");
>>        break;
>> Index: lib/StaticAnalyzer/Core/Environment.cpp
>> ===================================================================
>> --- lib/StaticAnalyzer/Core/Environment.cpp    (revision 129845)
>> +++ lib/StaticAnalyzer/Core/Environment.cpp    (working copy)
>> @@ -64,6 +64,9 @@
>>          else
>>            return svalBuilder.makeIntVal(cast<IntegerLiteral>(E));
>>        }
>> +      // For special C0xx nullptr case, make a null pointer SVal.
>> +      case Stmt::CXXNullPtrLiteralExprClass:
>> +        return svalBuilder.makeNullPtrVal();
>>        case Stmt::ImplicitCastExprClass:
>>        case Stmt::CXXFunctionalCastExprClass:
>>        case Stmt::CStyleCastExprClass: {
>> Index: lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
>> ===================================================================
>> --- lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp    (revision 129845)
>> +++ lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp    (working copy)
>> @@ -196,6 +196,10 @@
>>      return isFeasible ? state : NULL;
>>    }
>>
>> +  // Reverse assumption for special C0xx nullptr case.
>> +  case nonloc::NullPtrValKind:
>> +    return Assumption ? NULL : state;
>> +
>>    case nonloc::LocAsIntegerKind:
>>      return assumeAux(state, cast<nonloc::LocAsInteger>(Cond).getLoc(),
>>                       Assumption);
>> Index: lib/StaticAnalyzer/Core/Store.cpp
>> ===================================================================
>> --- lib/StaticAnalyzer/Core/Store.cpp    (revision 129845)
>> +++ lib/StaticAnalyzer/Core/Store.cpp    (working copy)
>> @@ -238,7 +238,7 @@
>> }
>>
>> SVal StoreManager::getLValueFieldOrIvar(const Decl* D, SVal Base) {
>> -  if (Base.isUnknownOrUndef())
>> +  if (Base.isUnknownOrUndef() || isa<nonloc::NullPtrVal>(Base))
>>      return Base;
>>
>>    Loc BaseL = cast<Loc>(Base);
>> @@ -280,7 +280,9 @@
>>    // FIXME: For absolute pointer addresses, we just return that value back 
>> as
>>    //  well, although in reality we should return the offset added to that
>>    //  value.
>> -  if (Base.isUnknownOrUndef() || isa<loc::ConcreteInt>(Base))
>> +  // Also, for nullptr, just return the base. Checker should detect this.
>> +  if (Base.isUnknownOrUndef() || isa<loc::ConcreteInt>(Base) ||
>> +            isa<nonloc::NullPtrVal>(Base))
>>      return Base;
>>
>>    const MemRegion* BaseRegion = cast<loc::MemRegionVal>(Base).getRegion();
>>
>> =====================
>> test/Analysis/nullptr.cpp
>> =====================
>> // RUN: %clang_cc1 -std=c++0x -analyze -analyzer-checker=core 
>> -analyzer-inline-call -analyzer-store region -verify %s
>>
>> // test to see if nullptr is detected as a null pointer
>> void foo1(void) {
>>   char  *np = nullptr;
>>   *np = 0;  // expected-warning{{Dereference of null pointer}}
>> }
>>
>> // check if comparing nullptr to nullptr is detected properly
>> void foo2(void) {
>>   char *np1 = nullptr;
>>   char *np2 = np1;
>>   char c;
>>   if (np1 == np2)
>>     np1 =&c;
>>   *np1 = 0;  // no-warning
>> }
>>
>> // invoving a nullptr in a more complex operation should be cause a warning
>> void foo3(void) {
>>   struct foo {
>>     int a, f;
>>   };
>>   char *np = nullptr;
>>   // casting a nullptr to anything should be caught eventually
>>   int *ip =&(((struct foo *)np)->f);
>>   *ip = 0;  // expected-warning{{Dereference of null pointer}}
>>   // should be error here too, but analysis gets stopped
>> //  *np = 0;
>> }
>>
>> #ifdef TEST_THIS
>> // check to determine if the correct function is picked
>> // which was the whole point of the nullptr to begin with
>> // FIXME: disabled for now until inline-call works again
>> struct A {
>>   int x;
>>   A(int a) { x = a; }
>>   A(int *ip) { x = 1; }
>>   int getx() const { return x; }
>> };
>>
>> void f1() {
>>   A x((int *)0);
>>   if (x.getx() == 0) {
>>     int *p = 0;
>>     *p = 3;  // warning
>>   } else {
>>     int *p = 0;
>>     *p = 3;  // no-warning
>>   }
>> }
>>
>> void f2() {
>>   A x(nullptr);
>>   if (x.getx() == 1) {
>>     int *p = 0;
>>     *p = 3;  // warning
>>   } else {
>>     int *p = 0;
>>     *p = 3;  // no-warning
>>   }
>> }
>> #endif
>>
>>
>> <nullptr.cpp><NullPtr.patch>
>
>
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to