[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-29 Thread Phabricator via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL340977: [CFG] [analyzer] Disable argument construction 
contexts for variadic functions. (authored by dergachev, committed by ).
Herald added a subscriber: llvm-commits.

Changed prior to commit:
  https://reviews.llvm.org/D50855?vs=162755=163202#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D50855

Files:
  cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
  cfe/trunk/test/Analysis/temporaries.cpp

Index: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -734,10 +734,14 @@
   ///
   /// If \p Result is provided, the new region will be bound to this expression
   /// instead of \p InitWithAdjustments.
-  ProgramStateRef createTemporaryRegionIfNeeded(ProgramStateRef State,
-const LocationContext *LC,
-const Expr *InitWithAdjustments,
-const Expr *Result = nullptr);
+  ///
+  /// Returns the temporary region with adjustments into the optional
+  /// OutRegionWithAdjustments out-parameter if a new region was indeed needed,
+  /// otherwise sets it to nullptr.
+  ProgramStateRef createTemporaryRegionIfNeeded(
+  ProgramStateRef State, const LocationContext *LC,
+  const Expr *InitWithAdjustments, const Expr *Result = nullptr,
+  const SubRegion **OutRegionWithAdjustments = nullptr);
 
   /// Returns a region representing the first element of a (possibly
   /// multi-dimensional) array, for the purposes of element construction or
Index: cfe/trunk/test/Analysis/temporaries.cpp
===
--- cfe/trunk/test/Analysis/temporaries.cpp
+++ cfe/trunk/test/Analysis/temporaries.cpp
@@ -1152,3 +1152,23 @@
 // and the non-definition decl should be found by direct lookup.
 void T::foo(C) {}
 } // namespace argument_virtual_decl_lookup
+
+namespace union_indirect_field_crash {
+union U {
+  struct {
+int x;
+  };
+};
+
+template  class C {
+public:
+  void foo() const {
+(void)(true ? U().x : 0);
+  }
+};
+
+void test() {
+  C c;
+  c.foo();
+}
+} // namespace union_indirect_field_crash
Index: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -283,11 +283,10 @@
   return state;
 }
 
-ProgramStateRef
-ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
-  const LocationContext *LC,
-  const Expr *InitWithAdjustments,
-  const Expr *Result) {
+ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
+ProgramStateRef State, const LocationContext *LC,
+const Expr *InitWithAdjustments, const Expr *Result,
+const SubRegion **OutRegionWithAdjustments) {
   // FIXME: This function is a hack that works around the quirky AST
   // we're often having with respect to C++ temporaries. If only we modelled
   // the actual execution order of statements properly in the CFG,
@@ -297,8 +296,11 @@
   if (!Result) {
 // If we don't have an explicit result expression, we're in "if needed"
 // mode. Only create a region if the current value is a NonLoc.
-if (!InitValWithAdjustments.getAs())
+if (!InitValWithAdjustments.getAs()) {
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = nullptr;
   return State;
+}
 Result = InitWithAdjustments;
   } else {
 // We need to create a region no matter what. For sanity, make sure we don't
@@ -418,11 +420,17 @@
   // The result expression would now point to the correct sub-region of the
   // newly created temporary region. Do this last in order to getSVal of Init
   // correctly in case (Result == Init).
-  State = State->BindExpr(Result, LC, Reg);
+  if (Result->isGLValue()) {
+State = State->BindExpr(Result, LC, Reg);
+  } else {
+State = State->BindExpr(Result, LC, InitValWithAdjustments);
+  }
 
   // Notify checkers once for two bindLoc()s.
   State = processRegionChange(State, TR, LC);
 
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = cast(Reg.getAsRegion());
   return State;
 }
 
@@ -2532,8 +2540,12 @@
   }
 
   // Handle regular struct fields / member variables.
-  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
-  SVal baseExprVal = state->getSVal(BaseExpr, LCtx);
+  const SubRegion *MR = nullptr;
+  state = 

[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-27 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 162755.
NoQ added a comment.

Address comments.


https://reviews.llvm.org/D50855

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/Analysis/temporaries.cpp

Index: test/Analysis/temporaries.cpp
===
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -1152,3 +1152,23 @@
 // and the non-definition decl should be found by direct lookup.
 void T::foo(C) {}
 } // namespace argument_virtual_decl_lookup
+
+namespace union_indirect_field_crash {
+union U {
+  struct {
+int x;
+  };
+};
+
+template  class C {
+public:
+  void foo() const {
+(void)(true ? U().x : 0);
+  }
+};
+
+void test() {
+  C c;
+  c.foo();
+}
+} // namespace union_indirect_field_crash
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -283,11 +283,10 @@
   return state;
 }
 
-ProgramStateRef
-ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
-  const LocationContext *LC,
-  const Expr *InitWithAdjustments,
-  const Expr *Result) {
+ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
+ProgramStateRef State, const LocationContext *LC,
+const Expr *InitWithAdjustments, const Expr *Result,
+const SubRegion **OutRegionWithAdjustments) {
   // FIXME: This function is a hack that works around the quirky AST
   // we're often having with respect to C++ temporaries. If only we modelled
   // the actual execution order of statements properly in the CFG,
@@ -297,8 +296,11 @@
   if (!Result) {
 // If we don't have an explicit result expression, we're in "if needed"
 // mode. Only create a region if the current value is a NonLoc.
-if (!InitValWithAdjustments.getAs())
+if (!InitValWithAdjustments.getAs()) {
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = nullptr;
   return State;
+}
 Result = InitWithAdjustments;
   } else {
 // We need to create a region no matter what. For sanity, make sure we don't
@@ -418,11 +420,17 @@
   // The result expression would now point to the correct sub-region of the
   // newly created temporary region. Do this last in order to getSVal of Init
   // correctly in case (Result == Init).
-  State = State->BindExpr(Result, LC, Reg);
+  if (Result->isGLValue()) {
+State = State->BindExpr(Result, LC, Reg);
+  } else {
+State = State->BindExpr(Result, LC, InitValWithAdjustments);
+  }
 
   // Notify checkers once for two bindLoc()s.
   State = processRegionChange(State, TR, LC);
 
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = cast(Reg.getAsRegion());
   return State;
 }
 
@@ -2532,8 +2540,12 @@
   }
 
   // Handle regular struct fields / member variables.
-  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
-  SVal baseExprVal = state->getSVal(BaseExpr, LCtx);
+  const SubRegion *MR = nullptr;
+  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr,
+/*Result=*/nullptr,
+/*OutRegionWithAdjustments=*/);
+  SVal baseExprVal =
+  MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, LCtx);
 
   const auto *field = cast(Member);
   SVal L = state->getLValue(field, baseExprVal);
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -734,10 +734,14 @@
   ///
   /// If \p Result is provided, the new region will be bound to this expression
   /// instead of \p InitWithAdjustments.
-  ProgramStateRef createTemporaryRegionIfNeeded(ProgramStateRef State,
-const LocationContext *LC,
-const Expr *InitWithAdjustments,
-const Expr *Result = nullptr);
+  ///
+  /// Returns the temporary region with adjustments into the optional
+  /// OutRegionWithAdjustments out-parameter if a new region was indeed needed,
+  /// otherwise sets it to nullptr.
+  ProgramStateRef createTemporaryRegionIfNeeded(
+  ProgramStateRef State, const LocationContext *LC,
+  const Expr *InitWithAdjustments, const Expr *Result = nullptr,
+  const SubRegion **OutRegionWithAdjustments = nullptr);
 
   /// Returns a region representing the first element of a (possibly
   /// multi-dimensional) array, for the purposes of element construction or
___

[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-21 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov requested changes to this revision.
george.karpenkov added a comment.
This revision now requires changes to proceed.

looks good apart from minor nits




Comment at: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h:744
+  const Expr *InitWithAdjustments, const Expr *Result = nullptr,
+  const SubRegion **OutRegionWithAdjustments = nullptr);
 

In general I would say that pair is still preferable, but I agree that with 
pre-c++17 codebase it gets too verbose.



Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:423
   // correctly in case (Result == Init).
-  State = State->BindExpr(Result, LC, Reg);
+  if (Result->isGLValue())
+State = State->BindExpr(Result, LC, Reg);

could we have braces here? Once we have "else" I would say we should have those.



Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:2543
   // Handle regular struct fields / member variables.
-  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
-  SVal baseExprVal = state->getSVal(BaseExpr, LCtx);
+  const SubRegion *MR;
+  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr,

Let's initialize that to nullptr


https://reviews.llvm.org/D50855



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

...and adding the aforementioned assertion for prvalues increases the  number 
of crashes on tests to 196. It seems that the analyzer assigns values of 
improper loc-less very often, but then immediately overwrites them :/ I wonder 
how much performance could be gained by fixing these bugs.


https://reviews.llvm.org/D50855



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

> which is of course a bad thing to do because we should not bind `Loc`s to 
> `prvalue` expressions

... of non-pointer type. On the other hand, binding `NonLoc`s to glvalue 
expressions is always a bad thing to do; but, for the reference, adding this as 
an assertion currently crashes 92 tests.


https://reviews.llvm.org/D50855



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 161073.
NoQ added a comment.

Add comments for optional arguments.

The reason why i chose an out-parameter rather than returning a std::pair is 
because most callers //presumably// don't need this feature.


https://reviews.llvm.org/D50855

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/Analysis/temporaries.cpp

Index: test/Analysis/temporaries.cpp
===
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -1152,3 +1152,23 @@
 // and the non-definition decl should be found by direct lookup.
 void T::foo(C) {}
 } // namespace argument_virtual_decl_lookup
+
+namespace union_indirect_field_crash {
+union U {
+  struct {
+int x;
+  };
+};
+
+template  class C {
+public:
+  void foo() const {
+(void)(true ? U().x : 0);
+  }
+};
+
+void test() {
+  C c;
+  c.foo();
+}
+} // namespace union_indirect_field_crash
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -283,11 +283,10 @@
   return state;
 }
 
-ProgramStateRef
-ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
-  const LocationContext *LC,
-  const Expr *InitWithAdjustments,
-  const Expr *Result) {
+ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
+ProgramStateRef State, const LocationContext *LC,
+const Expr *InitWithAdjustments, const Expr *Result,
+const SubRegion **OutRegionWithAdjustments) {
   // FIXME: This function is a hack that works around the quirky AST
   // we're often having with respect to C++ temporaries. If only we modelled
   // the actual execution order of statements properly in the CFG,
@@ -297,8 +296,11 @@
   if (!Result) {
 // If we don't have an explicit result expression, we're in "if needed"
 // mode. Only create a region if the current value is a NonLoc.
-if (!InitValWithAdjustments.getAs())
+if (!InitValWithAdjustments.getAs()) {
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = nullptr;
   return State;
+}
 Result = InitWithAdjustments;
   } else {
 // We need to create a region no matter what. For sanity, make sure we don't
@@ -418,11 +420,16 @@
   // The result expression would now point to the correct sub-region of the
   // newly created temporary region. Do this last in order to getSVal of Init
   // correctly in case (Result == Init).
-  State = State->BindExpr(Result, LC, Reg);
+  if (Result->isGLValue())
+State = State->BindExpr(Result, LC, Reg);
+  else
+State = State->BindExpr(Result, LC, InitValWithAdjustments);
 
   // Notify checkers once for two bindLoc()s.
   State = processRegionChange(State, TR, LC);
 
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = cast(Reg.getAsRegion());
   return State;
 }
 
@@ -2533,8 +2540,12 @@
   }
 
   // Handle regular struct fields / member variables.
-  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
-  SVal baseExprVal = state->getSVal(BaseExpr, LCtx);
+  const SubRegion *MR;
+  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr,
+/*Result=*/nullptr,
+/*OutRegionWithAdjustments=*/);
+  SVal baseExprVal =
+  MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, LCtx);
 
   const auto *field = cast(Member);
   SVal L = state->getLValue(field, baseExprVal);
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -734,10 +734,14 @@
   ///
   /// If \p Result is provided, the new region will be bound to this expression
   /// instead of \p InitWithAdjustments.
-  ProgramStateRef createTemporaryRegionIfNeeded(ProgramStateRef State,
-const LocationContext *LC,
-const Expr *InitWithAdjustments,
-const Expr *Result = nullptr);
+  ///
+  /// Returns the temporary region with adjustments into the optional
+  /// OutRegionWithAdjustments out-parameter if a new region was indeed needed,
+  /// otherwise sets it to nullptr.
+  ProgramStateRef createTemporaryRegionIfNeeded(
+  ProgramStateRef State, const LocationContext *LC,
+  const Expr *InitWithAdjustments, const Expr *Result = nullptr,
+  const SubRegion **OutRegionWithAdjustments = nullptr);
 
   /// Returns a region representing the first element 

[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, 
baloghadamsoftware.

Despite the effort to eliminate the need for 
`skipRValueSubobjectAdjustments()`, we still encounter ASTs that require it 
from time to time, for example in https://bugs.llvm.org/show_bug.cgi?id=37578

One way of properly modeling such expressions would be to include the 
member-expression "adjustment" into the //construction context// of the 
temporary, but that's not something i'm planning to do, because such ASTs are 
rare and seem to be only becoming more rare over time, so for now i'm just 
fixing the old code.

The root cause of the problem in this example is that while evaluating the 
`MemberExpr` in

  `-MemberExpr 0x7fd5ef0035b8  'a::(anonymous struct at 
test.cpp:2:3)' . 0x7fd5ee06a068
`-CXXTemporaryObjectExpr 0x7fd5ef001f70  'a' 'void () 
noexcept' zeroing

there's no way for `createTemporaryRegionIfNeeded()` to communicate the newly 
created temporary region through the Environment (as it usually does), because 
all expressions so far have been prvalues.

The current code works around that problem by binding the region to the 
`CXXTemporaryObjectExpr`, which is of course a bad thing to do because we 
should not bind `Loc`s to prvalue expressions, and it leads to a crash when 
eventually this bad binding propagates to the Store and the Store is unable to 
load it.

The solution is to bind the correct [lazy compound] value to the 
`CXXTemporaryObjectExpr` and then communicate the region to the caller directly 
via an out-parameter.


Repository:
  rC Clang

https://reviews.llvm.org/D50855

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/Analysis/temporaries.cpp

Index: test/Analysis/temporaries.cpp
===
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -1152,3 +1152,23 @@
 // and the non-definition decl should be found by direct lookup.
 void T::foo(C) {}
 } // namespace argument_virtual_decl_lookup
+
+namespace union_indirect_field_crash {
+union U {
+  struct {
+int x;
+  };
+};
+
+template  class C {
+public:
+  void foo() const {
+(void)(true ? U().x : 0);
+  }
+};
+
+void test() {
+  C c;
+  c.foo();
+}
+} // namespace union_indirect_field_crash
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -283,11 +283,10 @@
   return state;
 }
 
-ProgramStateRef
-ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
-  const LocationContext *LC,
-  const Expr *InitWithAdjustments,
-  const Expr *Result) {
+ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
+ProgramStateRef State, const LocationContext *LC,
+const Expr *InitWithAdjustments, const Expr *Result,
+const SubRegion **OutRegionWithAdjustments) {
   // FIXME: This function is a hack that works around the quirky AST
   // we're often having with respect to C++ temporaries. If only we modelled
   // the actual execution order of statements properly in the CFG,
@@ -297,8 +296,11 @@
   if (!Result) {
 // If we don't have an explicit result expression, we're in "if needed"
 // mode. Only create a region if the current value is a NonLoc.
-if (!InitValWithAdjustments.getAs())
+if (!InitValWithAdjustments.getAs()) {
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = nullptr;
   return State;
+}
 Result = InitWithAdjustments;
   } else {
 // We need to create a region no matter what. For sanity, make sure we don't
@@ -418,11 +420,16 @@
   // The result expression would now point to the correct sub-region of the
   // newly created temporary region. Do this last in order to getSVal of Init
   // correctly in case (Result == Init).
-  State = State->BindExpr(Result, LC, Reg);
+  if (Result->isGLValue())
+State = State->BindExpr(Result, LC, Reg);
+  else
+State = State->BindExpr(Result, LC, InitValWithAdjustments);
 
   // Notify checkers once for two bindLoc()s.
   State = processRegionChange(State, TR, LC);
 
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = cast(Reg.getAsRegion());
   return State;
 }
 
@@ -2533,8 +2540,11 @@
   }
 
   // Handle regular struct fields / member variables.
-  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
-  SVal baseExprVal = state->getSVal(BaseExpr, LCtx);
+  const SubRegion *MR;
+  state =
+  createTemporaryRegionIfNeeded(state, LCtx, BaseExpr, nullptr, );
+  SVal baseExprVal =
+  MR ? loc::MemRegionVal(MR) :