[cfe-commits] [PATCH] CallExprs returning references can be lvalues

Jordy Rose jediknil at belkadan.com
Wed Jun 2 15:19:25 PDT 2010


Fixes a crasher, by allowing CallExprs to be lvalues, and making sure
assignment LHSes are treated as lvalues.

char& ref();
void t5 () {
  ref() = 'c';
}

And an incorrect "undefined dereference" warning, by noting that assigning
to a reference variable does not kill its value.

char t1 () {
  char& r = ref();
  r = 'c'; // no-warning
  if (r) return r;
  return *(char*)0; // no-warning
}
-------------- next part --------------
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp	(revision 105255)
+++ lib/Analysis/CFG.cpp	(working copy)
@@ -538,6 +538,15 @@
     addStmt(B->getRHS());
     return addStmt(B->getLHS());
   }
+  else if (B->isAssignmentOp()) {
+    if (asc.alwaysAdd()) {
+      autoCreateBlock();
+      AppendStmt(Block, B, asc);
+    }
+    
+    addStmt(B->getRHS());
+    return addStmt(B->getLHS(), AddStmtChoice::AsLValueNotAlwaysAdd);
+  }
 
   return VisitStmt(B, asc);
 }
@@ -612,8 +621,12 @@
   if (!CanThrow(C->getCallee()))
     AddEHEdge = false;
 
-  if (!NoReturn && !AddEHEdge)
-    return VisitStmt(C, AddStmtChoice::AlwaysAdd);
+  if (!NoReturn && !AddEHEdge) {
+    if (asc.asLValue())
+      return VisitStmt(C, AddStmtChoice::AlwaysAddAsLValue);
+    else
+      return VisitStmt(C, AddStmtChoice::AlwaysAdd);
+  }
 
   if (Block) {
     Succ = Block;
Index: lib/Analysis/LiveVariables.cpp
===================================================================
--- lib/Analysis/LiveVariables.cpp	(revision 105255)
+++ lib/Analysis/LiveVariables.cpp	(working copy)
@@ -256,17 +256,21 @@
 
   // Assigning to a variable?
   if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS->IgnoreParens())) {
+    // Assignments to references don't kill the ref's address
+    if (DR->getDecl()->getType()->isReferenceType()) {
+      VisitDeclRefExpr(DR);
+    } else {
+      // Update liveness inforamtion.
+      unsigned bit = AD.getIdx(DR->getDecl());
+      LiveState.getDeclBit(bit) = Dead | AD.AlwaysLive.getDeclBit(bit);
 
-    // Update liveness inforamtion.
-    unsigned bit = AD.getIdx(DR->getDecl());
-    LiveState.getDeclBit(bit) = Dead | AD.AlwaysLive.getDeclBit(bit);
+      if (AD.Observer) { AD.Observer->ObserverKill(DR); }
 
-    if (AD.Observer) { AD.Observer->ObserverKill(DR); }
-
-    // Handle things like +=, etc., which also generate "uses"
-    // of a variable.  Do this just by visiting the subexpression.
-    if (B->getOpcode() != BinaryOperator::Assign)
-      VisitDeclRefExpr(DR);
+      // Handle things like +=, etc., which also generate "uses"
+      // of a variable.  Do this just by visiting the subexpression.
+      if (B->getOpcode() != BinaryOperator::Assign)
+        VisitDeclRefExpr(DR);
+    }
   }
   else // Not assigning to a variable.  Process LHS as usual.
     Visit(LHS);
@@ -290,8 +294,14 @@
       // transfer function for this expression after the DeclStmt.  If the
       // initializer references the variable (which is bad) then we extend
       // its liveness.
-      if (Expr* Init = VD->getInit())
+      if (Expr* Init = VD->getInit()) {
+        // if (VD->getType()->isReferenceType()) {
+        //   if (ImplicitCastExpr* Cast = dyn_cast<ImplicitCastExpr>(Init))
+        //     Cast->setLvalueCast(true);
+        // }
+
         Visit(Init);
+      }
 
       if (const VariableArrayType* VT =
             AD.getContext().getAsVariableArrayType(VD->getType())) {
Index: test/Analysis/reference.cpp
===================================================================
--- test/Analysis/reference.cpp	(revision 105255)
+++ test/Analysis/reference.cpp	(working copy)
@@ -9,3 +9,48 @@
   if (b != 3)
     *p = 1; // no-warning
 }
+
+typedef typeof(sizeof(int)) size_t;
+void malloc (size_t);
+
+char* ptr();
+char& ref();
+
+// Each of the tests below is repeated with pointers as well as references.
+// This is mostly a sanity check, but then again, both should work!
+char t1 () {
+  char& r = ref();
+  r = 'c'; // no-warning
+  if (r) return r;
+  return *(char*)0; // no-warning
+}
+
+char t2 () {
+  char* p = ptr();
+  *p = 'c'; // no-warning
+  if (*p) return *p;
+  return *(char*)0; // no-warning
+}
+
+char t3 (char& r) {
+  r = 'c'; // no-warning
+  if (r) return r;
+  return *(char*)0; // no-warning
+}
+
+char t4 (char* p) {
+  *p = 'c'; // no-warning
+  if (*p) return *p;
+  return *(char*)0; // no-warning
+}
+
+// These next two tests just shouldn't crash.
+char t5 () {
+  ref() = 'c';
+  return '0';
+}
+
+char t6 () {
+  *ptr() = 'c';
+  return '0';
+}


More information about the cfe-commits mailing list