]> granicus.if.org Git - clang/commitdiff
Improve our handling of (C++) references within Clang. Specifically:
authorDouglas Gregor <dgregor@apple.com>
Tue, 28 Oct 2008 00:22:11 +0000 (00:22 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 28 Oct 2008 00:22:11 +0000 (00:22 +0000)
  - Do not allow expressions to ever have reference type
  - Extend Expr::isLvalue to handle more cases where having written a
    reference into the source implies that the expression is an lvalue
    (e.g., function calls, C++ casts).
  - Make GRExprEngine::VisitCall treat the call arguments as lvalues when
    they are being bound to a reference parameter.

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

include/clang/AST/Expr.h
include/clang/AST/ExprCXX.h
include/clang/AST/StmtNodes.def
include/clang/Analysis/PathSensitive/GRExprEngine.h
lib/AST/Expr.cpp
lib/Analysis/GRExprEngine.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprCXX.cpp

index 70a768171eff40d264468b6da6bf7f9ca4b7d12e..79cb76012eae7621968bc66c791a66a1ffb95e45 100644 (file)
@@ -38,7 +38,16 @@ namespace clang {
 class Expr : public Stmt {
   QualType TR;
 protected:
-  Expr(StmtClass SC, QualType T) : Stmt(SC), TR(T) {}
+  Expr(StmtClass SC, QualType T) : Stmt(SC), TR(T) {
+    // In C++, the type of an expression is always adjusted so that it
+    // will not have reference type an expression will never have
+    // reference type (C++ [expr]p6). Use
+    // QualType::getNonReferenceType() to retrieve the non-reference
+    // type. Additionally, inspect Expr::isLvalue to determine whether
+    // an expression that is adjusted in this manner should be
+    // considered an lvalue.
+    assert((T.isNull() || !T->isReferenceType()) && "Expressions can't have reference type");
+  }
 public:  
   QualType getType() const { return TR; }
   void setType(QualType t) { TR = t; }
@@ -786,20 +795,14 @@ public:
   const Expr *getSubExpr() const { return cast<Expr>(Op); }
   
   static bool classof(const Stmt *T) { 
-    switch (T->getStmtClass()) {
-    case ImplicitCastExprClass:
-    case ExplicitCastExprClass:
-    case ExplicitCCastExprClass:
-    case CXXNamedCastExprClass:
-    case CXXStaticCastExprClass:
-    case CXXDynamicCastExprClass:
-    case CXXReinterpretCastExprClass:
-    case CXXConstCastExprClass:
-    case CXXFunctionalCastExprClass:
+    StmtClass SC = T->getStmtClass();
+    if (SC >= CXXNamedCastExprClass && SC <= CXXFunctionalCastExprClass)
+      return true;
+
+    if (SC >= ImplicitCastExprClass && SC <= ExplicitCCastExprClass)
       return true;
-    default:
-      return false;
-    }
+
+    return false;
   }
   static bool classof(const CastExpr *) { return true; }
   
@@ -862,18 +865,13 @@ public:
   QualType getTypeAsWritten() const { return TypeAsWritten; }
 
   static bool classof(const Stmt *T) { 
-    switch (T->getStmtClass()) {
-    case ExplicitCastExprClass:
-    case ExplicitCCastExprClass:
-    case CXXFunctionalCastExprClass:
-    case CXXStaticCastExprClass:
-    case CXXDynamicCastExprClass:
-    case CXXReinterpretCastExprClass:
-    case CXXConstCastExprClass:
+    StmtClass SC = T->getStmtClass();
+    if (SC >= ExplicitCastExprClass && SC <= ExplicitCCastExprClass)
+      return true;
+    if (SC >= CXXNamedCastExprClass && SC <= CXXFunctionalCastExprClass)
       return true;
-    default:
-      return false;
-    }
+
+    return false;
   }
   static bool classof(const ExplicitCastExpr *) { return true; }
 };
index ef5f95d09cb89225a93f48bd1dfcbbbace6fbbfc..61d25e9a97b35ab590edc68ad2f292b84b399aa2 100644 (file)
@@ -301,7 +301,8 @@ class CXXConditionDeclExpr : public DeclRefExpr {
 public:
   CXXConditionDeclExpr(SourceLocation startLoc,
                        SourceLocation eqLoc, VarDecl *var)
-    : DeclRefExpr(CXXConditionDeclExprClass, var, var->getType(), startLoc) {}
+    : DeclRefExpr(CXXConditionDeclExprClass, var, 
+                  var->getType().getNonReferenceType(), startLoc) {}
 
   virtual void Destroy(ASTContext& Ctx);
 
index a79639eac049c224f42e6a0f7eb90541d747bf53..a1403915c9a39e35d47addb0d4aa10c688e6ec64 100644 (file)
@@ -95,10 +95,10 @@ STMT(61, CXXStaticCastExpr      , CXXNamedCastExpr)
 STMT(62, CXXDynamicCastExpr     , CXXNamedCastExpr)
 STMT(63, CXXReinterpretCastExpr , CXXNamedCastExpr)
 STMT(64, CXXConstCastExpr       , CXXNamedCastExpr)
-STMT(65, CXXBoolLiteralExpr     , Expr)
-STMT(66, CXXThrowExpr           , Expr)
-STMT(67, CXXDefaultArgExpr      , Expr)
-STMT(68, CXXFunctionalCastExpr  , Expr)
+STMT(65, CXXFunctionalCastExpr  , Expr)
+STMT(66, CXXBoolLiteralExpr     , Expr)
+STMT(67, CXXThrowExpr           , Expr)
+STMT(68, CXXDefaultArgExpr      , Expr)
 STMT(69, CXXZeroInitValueExpr   , Expr)
 STMT(70, CXXConditionDeclExpr   , DeclRefExpr)
 
index 22454810bef8f869df422ebca114e4643c43b1ba..802b07e31ff825115a0c20a3ccd26bdebcfdb753 100644 (file)
@@ -495,6 +495,10 @@ protected:
   void VisitCall(CallExpr* CE, NodeTy* Pred,
                  CallExpr::arg_iterator AI, CallExpr::arg_iterator AE,
                  NodeSet& Dst);
+  void VisitCallRec(CallExpr* CE, NodeTy* Pred,
+                    CallExpr::arg_iterator AI, CallExpr::arg_iterator AE,
+                    NodeSet& Dst, const FunctionTypeProto *, 
+                    unsigned ParamIdx = 0);
   
   /// VisitCast - Transfer function logic for all casts (implicit and explicit).
   void VisitCast(Expr* CastE, Expr* Ex, NodeTy* Pred, NodeSet& Dst);
index 1380264e0e76613d44dbe9c1562568ca0e1547b5..f330444f1d40e82e7073c540d1e4fb493aa4cb79 100644 (file)
@@ -393,6 +393,20 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
     break;
   case ParenExprClass: // C99 6.5.1p5
     return cast<ParenExpr>(this)->getSubExpr()->isLvalue(Ctx);
+  case CallExprClass: {
+    // C++ [expr.call]p10:
+    //   A function call is an lvalue if and only if the result type
+    //   is a reference.
+    QualType CalleeType 
+      = dyn_cast<CallExpr>(this)->getCallee()->IgnoreParens()->getType();
+    if (const PointerType *FnTypePtr = CalleeType->getAsPointerType())
+      if (const FunctionType *FnType
+            = FnTypePtr->getPointeeType()->getAsFunctionType())
+        if (FnType->getResultType()->isReferenceType())
+          return LV_Valid;
+    
+    break;
+  }
   case CompoundLiteralExprClass: // C99 6.5.2.5p5
     return LV_Valid;
   case ExtVectorElementExprClass:
@@ -407,10 +421,25 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
     return (cast<PredefinedExpr>(this)->getIdentType()
                == PredefinedExpr::CXXThis
             ? LV_InvalidExpression : LV_Valid);
+  case VAArgExprClass:
+    return LV_Valid;
   case CXXDefaultArgExprClass:
     return cast<CXXDefaultArgExpr>(this)->getExpr()->isLvalue(Ctx);
   case CXXConditionDeclExprClass:
     return LV_Valid;
+  case ExplicitCCastExprClass:
+  case CXXFunctionalCastExprClass:
+  case CXXStaticCastExprClass:
+  case CXXDynamicCastExprClass:
+  case CXXReinterpretCastExprClass:
+  case CXXConstCastExprClass:
+    // The result of an explicit cast is an lvalue if the type we are
+    // casting to is a reference type. See C++ [expr.cast]p1, 
+    // C++ [expr.static.cast]p2, C++ [expr.dynamic.cast]p2,
+    // C++ [expr.reinterpret.cast]p1, C++ [expr.const.cast]p1.
+    if (cast<ExplicitCastExpr>(this)->getTypeAsWritten()->isReferenceType())
+      return LV_Valid;
+    break;
   default:
     break;
   }
index e843f5d9e17a0a502bdedccb7840910d5f1b4dec..41579ac3a7132e5aaaf5d4ff9058b3e46ad04341 100644 (file)
@@ -1063,22 +1063,43 @@ const GRState* GRExprEngine::EvalLocation(Expr* Ex, NodeTy* Pred,
 //===----------------------------------------------------------------------===//
 // Transfer function: Function calls.
 //===----------------------------------------------------------------------===//
-
 void GRExprEngine::VisitCall(CallExpr* CE, NodeTy* Pred,
                              CallExpr::arg_iterator AI,
                              CallExpr::arg_iterator AE,
-                             NodeSet& Dst) {
+                             NodeSet& Dst)
+{
+  // Determine the type of function we're calling (if available).
+  const FunctionTypeProto *Proto = NULL;
+  QualType FnType = CE->getCallee()->IgnoreParens()->getType();
+  if (const PointerType *FnTypePtr = FnType->getAsPointerType())
+    Proto = FnTypePtr->getPointeeType()->getAsFunctionTypeProto();
+
+  VisitCallRec(CE, Pred, AI, AE, Dst, Proto, /*ParamIdx=*/0);
+}
+
+void GRExprEngine::VisitCallRec(CallExpr* CE, NodeTy* Pred,
+                                CallExpr::arg_iterator AI,
+                                CallExpr::arg_iterator AE,
+                                NodeSet& Dst, const FunctionTypeProto *Proto,
+                                unsigned ParamIdx) {
   
   // Process the arguments.
-  
   if (AI != AE) {
-    
-    NodeSet DstTmp;      
-    Visit(*AI, Pred, DstTmp);    
+    // If the call argument is being bound to a reference parameter,
+    // visit it as an lvalue, not an rvalue.
+    bool VisitAsLvalue = false;
+    if (Proto && ParamIdx < Proto->getNumArgs())
+      VisitAsLvalue = Proto->getArgType(ParamIdx)->isReferenceType();
+
+    NodeSet DstTmp;  
+    if (VisitAsLvalue)
+      VisitLValue(*AI, Pred, DstTmp);    
+    else
+      Visit(*AI, Pred, DstTmp);    
     ++AI;
     
     for (NodeSet::iterator DI=DstTmp.begin(), DE=DstTmp.end(); DI != DE; ++DI)
-      VisitCall(CE, *DI, AI, AE, Dst);
+      VisitCallRec(CE, *DI, AI, AE, Dst, Proto, ParamIdx + 1);
     
     return;
   }
index 7b88ec2c1694f082cc1a2c1de4f44c159b9ee74f..3e33172c8ab2a7367e78ffb0c240318f76c473df 100644 (file)
@@ -1382,11 +1382,6 @@ bool Sema::CheckForConstantInitializer(Expr *Init, QualType DclT) {
   if (CompoundLiteralExpr *e = dyn_cast<CompoundLiteralExpr>(Init))
     return CheckForConstantInitializer(e->getInitializer(), DclT);
 
-  if (Init->getType()->isReferenceType()) {
-    // FIXME: Work out how the heck references work.
-    return false;
-  }
-
   if (InitListExpr *Exp = dyn_cast<InitListExpr>(Init)) {
     unsigned numInits = Exp->getNumInits();
     for (unsigned i = 0; i < numInits; i++) {
index a73f0dde0a3c2eb8ca47ab1b6ef32279f37440bc..42f9ec727efeea91d95465228810bb7465dbe53d 100644 (file)
@@ -35,10 +35,6 @@ void Sema::DefaultFunctionArrayConversion(Expr *&E) {
   QualType Ty = E->getType();
   assert(!Ty.isNull() && "DefaultFunctionArrayConversion - missing type");
 
-  if (const ReferenceType *ref = Ty->getAsReferenceType()) {
-    ImpCastExprToType(E, ref->getPointeeType()); // C++ [expr]
-    Ty = E->getType();
-  }
   if (Ty->isFunctionType())
     ImpCastExprToType(E, Context.getPointerType(Ty));
   else if (Ty->isArrayType()) {
@@ -68,10 +64,6 @@ Expr *Sema::UsualUnaryConversions(Expr *&Expr) {
   QualType Ty = Expr->getType();
   assert(!Ty.isNull() && "UsualUnaryConversions - missing type");
   
-  if (const ReferenceType *Ref = Ty->getAsReferenceType()) {
-    ImpCastExprToType(Expr, Ref->getPointeeType()); // C++ [expr]
-    Ty = Expr->getType();
-  }
   if (Ty->isPromotableIntegerType()) // C99 6.3.1.1p2
     ImpCastExprToType(Expr, Context.IntTy);
   else
@@ -442,11 +434,13 @@ Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc,
   if (CurBlock && ShouldSnapshotBlockValueReference(CurBlock, VD)) {
     // The BlocksAttr indicates the variable is bound by-reference.
     if (VD->getAttr<BlocksAttr>())
-      return new BlockDeclRefExpr(VD, VD->getType(), Loc, true);
+      return new BlockDeclRefExpr(VD, VD->getType().getNonReferenceType(), 
+                                  Loc, true);
       
     // Variable will be bound by-copy, make it const within the closure.
     VD->getType().addConst();
-    return new BlockDeclRefExpr(VD, VD->getType(), Loc, false);
+    return new BlockDeclRefExpr(VD, VD->getType().getNonReferenceType(), 
+                                Loc, false);
   }
   // If this reference is not in a block or if the referenced variable is
   // within the block, create a normal DeclRefExpr.
@@ -1674,8 +1668,15 @@ Sema::CheckAssignmentConstraints(QualType lhsType, QualType rhsType) {
   if (lhsType == rhsType)
     return Compatible; // Common case: fast path an exact match.
 
-  if (lhsType->isReferenceType() || rhsType->isReferenceType()) {
-    if (Context.typesAreCompatible(lhsType, rhsType))
+  // If the left-hand side is a reference type, then we are in a
+  // (rare!) case where we've allowed the use of references in C,
+  // e.g., as a parameter type in a built-in function. In this case,
+  // just make sure that the type referenced is compatible with the
+  // right-hand side type. The caller is responsible for adjusting
+  // lhsType so that the resulting expression does not have reference
+  // type.
+  if (const ReferenceType *lhsTypeRef = lhsType->getAsReferenceType()) {
+    if (Context.typesAreCompatible(lhsTypeRef->getPointeeType(), rhsType))
       return Compatible;
     return Incompatible;
   }
@@ -1808,8 +1809,7 @@ Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) {
   // DeclExpr's (created by ActOnIdentifierExpr), it would mess up the unary
   // expressions that surpress this implicit conversion (&, sizeof).
   //
-  // Suppress this for references: C++ 8.5.3p5.  FIXME: revisit when references
-  // are better understood.
+  // Suppress this for references: C++ 8.5.3p5.  
   if (!lhsType->isReferenceType())
     DefaultFunctionArrayConversion(rExpr);
 
@@ -1818,8 +1818,12 @@ Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) {
   
   // C99 6.5.16.1p2: The value of the right operand is converted to the
   // type of the assignment expression.
+  // CheckAssignmentConstraints allows the left-hand side to be a reference,
+  // so that we can use references in built-in functions even in C.
+  // The getNonReferenceType() call makes sure that the resulting expression
+  // does not have reference type.
   if (rExpr->getType() != lhsType)
-    ImpCastExprToType(rExpr, lhsType);
+    ImpCastExprToType(rExpr, lhsType.getNonReferenceType());
   return result;
 }
 
@@ -2909,7 +2913,8 @@ Sema::ExprResult Sema::ActOnBuiltinOffsetOf(SourceLocation BuiltinLoc,
     // FIXME: Verify that MemberDecl isn't a bitfield.
     // MemberDecl->getType() doesn't get the right qualifiers, but it doesn't
     // matter here.
-    Res = new MemberExpr(Res, false, MemberDecl, OC.LocEnd, MemberDecl->getType());
+    Res = new MemberExpr(Res, false, MemberDecl, OC.LocEnd, 
+                         MemberDecl->getType().getNonReferenceType());
   }
   
   return new UnaryOperator(Res, UnaryOperator::OffsetOf, Context.getSizeType(),
@@ -3121,7 +3126,8 @@ Sema::ExprResult Sema::ActOnOverloadExpr(ExprTy **args, unsigned NumArgs,
                     OE->getFn()->getSourceRange());
       // Remember our match, and continue processing the remaining arguments
       // to catch any errors.
-      OE = new OverloadExpr(Args, NumArgs, i, FnType->getResultType(),
+      OE = new OverloadExpr(Args, NumArgs, i, 
+                            FnType->getResultType().getNonReferenceType(),
                             BuiltinLoc, RParenLoc);
     }
   }
@@ -3168,7 +3174,7 @@ Sema::ExprResult Sema::ActOnVAArg(SourceLocation BuiltinLoc,
   
   // FIXME: Warn if a non-POD type is passed in.
   
-  return new VAArgExpr(BuiltinLoc, E, T, RPLoc);
+  return new VAArgExpr(BuiltinLoc, E, T.getNonReferenceType(), RPLoc);
 }
 
 bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
index 34eff25b4c2b02a93b872e1e5cf43715b460da4d..7a5e2dd4e91ff38697f70b34dccf59aa3fa65438 100644 (file)
@@ -79,10 +79,6 @@ Sema::CheckConstCast(SourceLocation OpLoc, Expr *&SrcExpr, QualType DestType)
     // C++ 5.2.11p4: An lvalue of type T1 can be [cast] to an lvalue of type T2
     //   [...] if a pointer to T1 can be [cast] to the type pointer to T2.
     DestType = Context.getPointerType(DestTypeTmp->getPointeeType());
-    if (const ReferenceType *SrcTypeTmp = SrcType->getAsReferenceType()) {
-      // FIXME: This shouldn't actually be possible, but right now it is.
-      SrcType = SrcTypeTmp->getPointeeType();
-    }
     SrcType = Context.getPointerType(SrcType);
   } else {
     // C++ 5.2.11p1: Otherwise, the result is an rvalue and the
@@ -177,10 +173,6 @@ Sema::CheckReinterpretCast(SourceLocation OpLoc, Expr *&SrcExpr,
     //   built-in & and * operators.
     // This code does this transformation for the checked types.
     DestType = Context.getPointerType(DestTypeTmp->getPointeeType());
-    if (const ReferenceType *SrcTypeTmp = SrcType->getAsReferenceType()) {
-      // FIXME: This shouldn't actually be possible, but right now it is.
-      SrcType = SrcTypeTmp->getPointeeType();
-    }
     SrcType = Context.getPointerType(SrcType);
   } else {
     // C++ 5.2.10p1: [...] the lvalue-to-rvalue, array-to-pointer, and