]> granicus.if.org Git - clang/commitdiff
[analyzer] Be careful about LazyCompoundVals, which may be for the first field.
authorJordan Rose <jordan_rose@apple.com>
Fri, 6 Jul 2012 21:59:56 +0000 (21:59 +0000)
committerJordan Rose <jordan_rose@apple.com>
Fri, 6 Jul 2012 21:59:56 +0000 (21:59 +0000)
We use LazyCompoundVals to avoid copying the contents of structs and arrays
around in the store, and when we need to pass a struct around that already
has a LazyCompoundVal we just use the original one. However, it's possible
that the first field of a struct may have a LazyCompoundVal of its own, and
we currently can't distinguish a LazyCompoundVal for the first element of a
struct from a LazyCompoundVal for the entire struct. In this case we should
just drop the optimization and make a new LazyCompoundVal that encompasses
the old one.

PR13264 / <rdar://problem/11802440>

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159866 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Core/RegionStore.cpp
test/Analysis/array-struct-region.c

index a8e47ff8e2302f721cb469f19dbcb17769719506..fa26c132039b63b0df7146377aa9f2d6fd4f0a13 100644 (file)
@@ -1447,16 +1447,27 @@ SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) {
   return svalBuilder.getRegionValueSymbolVal(R);
 }
 
+static bool mayHaveLazyBinding(QualType Ty) {
+  return Ty->isArrayType() || Ty->isStructureOrClassType();
+}
+
 SVal RegionStoreManager::getBindingForStruct(Store store, 
                                         const TypedValueRegion* R) {
-  assert(R->getValueType()->isStructureOrClassType());
-  
-  // If we already have a lazy binding, don't create a new one.
-  RegionBindings B = GetRegionBindings(store);
-  BindingKey K = BindingKey::Make(R, BindingKey::Default);
-  if (const nonloc::LazyCompoundVal *V =
-      dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) {
-    return *V;
+  const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl();
+  if (RD->field_empty())
+    return UnknownVal();
+
+  // If we already have a lazy binding, don't create a new one,
+  // unless the first field might have a lazy binding of its own.
+  // (Right now we can't tell the difference.)
+  QualType FirstFieldType = RD->field_begin()->getType();
+  if (!mayHaveLazyBinding(FirstFieldType)) {
+    RegionBindings B = GetRegionBindings(store);
+    BindingKey K = BindingKey::Make(R, BindingKey::Default);
+    if (const nonloc::LazyCompoundVal *V =
+          dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) {
+      return *V;
+    }
   }
 
   return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
@@ -1464,14 +1475,19 @@ SVal RegionStoreManager::getBindingForStruct(Store store,
 
 SVal RegionStoreManager::getBindingForArray(Store store,
                                        const TypedValueRegion * R) {
-  assert(Ctx.getAsConstantArrayType(R->getValueType()));
+  const ConstantArrayType *Ty = Ctx.getAsConstantArrayType(R->getValueType());
+  assert(Ty && "Only constant array types can have compound bindings.");
   
-  // If we already have a lazy binding, don't create a new one.
-  RegionBindings B = GetRegionBindings(store);
-  BindingKey K = BindingKey::Make(R, BindingKey::Default);
-  if (const nonloc::LazyCompoundVal *V =
-      dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) {
-    return *V;
+  // If we already have a lazy binding, don't create a new one,
+  // unless the first element might have a lazy binding of its own.
+  // (Right now we can't tell the difference.)
+  if (!mayHaveLazyBinding(Ty->getElementType())) {
+    RegionBindings B = GetRegionBindings(store);
+    BindingKey K = BindingKey::Make(R, BindingKey::Default);
+    if (const nonloc::LazyCompoundVal *V =
+        dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) {
+      return *V;
+    }
   }
 
   return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
index c1eddcdd217f04874250bac9435a69d594bfe4d8..ddb9f4b1167cc529eb3c4e374dbd7d94a50bfd79 100644 (file)
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-store=region -analyzer-constraints=basic -verify %s
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-store=region -analyzer-constraints=range -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-store=region -analyzer-constraints=basic -analyzer-ipa=all -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-store=region -analyzer-constraints=range -analyzer-ipa=all -verify %s
 
 void clang_analyzer_eval(int);
 
@@ -57,3 +57,38 @@ void struct_as_array() {
   clang_analyzer_eval(p->y == 5); // expected-warning{{TRUE}}
 }
 
+
+// PR13264 / <rdar://problem/11802440>
+struct point { int x; int y; };
+struct circle { struct point o; int r; };
+struct circle get_circle() {
+  struct circle result;
+  result.r = 5;
+  result.o = (struct point){0, 0};
+  return result;
+}
+
+void struct_in_struct() {
+  struct circle c;
+  c = get_circle();
+  // This used to think c.r was undefined because c.o is a LazyCompoundVal.
+  clang_analyzer_eval(c.r == 5); // expected-warning{{TRUE}}
+}
+
+// We also test with floats because we don't model floats right now,
+// and the original bug report used a float.
+struct circle_f { struct point o; float r; };
+struct circle_f get_circle_f() {
+  struct circle_f result;
+  result.r = 5.0;
+  result.o = (struct point){0, 0};
+  return result;
+}
+
+float struct_in_struct_f() {
+  struct circle_f c;
+  c = get_circle_f();
+
+  return c.r; // no-warning
+}
+