]> granicus.if.org Git - clang/commitdiff
Make IgnoreParens() look through ChooseExprs.
authorEli Friedman <eli.friedman@gmail.com>
Sat, 20 Jul 2013 00:40:58 +0000 (00:40 +0000)
committerEli Friedman <eli.friedman@gmail.com>
Sat, 20 Jul 2013 00:40:58 +0000 (00:40 +0000)
This is the same way GenericSelectionExpr works, and it's generally a
more consistent approach.

A large part of this patch is devoted to caching the value of the condition
of a ChooseExpr; it's needed to avoid threading an ASTContext into
IgnoreParens().

Fixes <rdar://problem/14438917>.

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

17 files changed:
include/clang/AST/EvaluatedExprVisitor.h
include/clang/AST/Expr.h
lib/AST/Expr.cpp
lib/AST/ExprClassification.cpp
lib/AST/ExprConstant.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CGExprAgg.cpp
lib/CodeGen/CGExprComplex.cpp
lib/CodeGen/CGExprConstant.cpp
lib/CodeGen/CGExprScalar.cpp
lib/Sema/SemaExceptionSpec.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaPseudoObject.cpp
lib/Serialization/ASTReaderStmt.cpp
lib/Serialization/ASTWriterStmt.cpp
lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp
test/SemaObjC/property-choose-expr.m [new file with mode: 0644]

index 2e3cbfad919dea0ebea26f503018aeb0202fe509..12c4fcc49b0132864225758ef37988678f03ef68 100644 (file)
@@ -53,7 +53,7 @@ public:
     if (E->getCond()->isValueDependent())
       return;
     // Only the selected subexpression matters; the other one is not evaluated.
-    return this->Visit(E->getChosenSubExpr(Context));
+    return this->Visit(E->getChosenSubExpr());
   }
 
   void VisitDesignatedInitExpr(DesignatedInitExpr *E) {
index d035fbdf662e82153a7cd9dd0e568ed3472ecdbd..42c99bbee099bb6c2fe9c8195ddeb2af37c4e6bc 100644 (file)
@@ -3475,10 +3475,12 @@ class ChooseExpr : public Expr {
   enum { COND, LHS, RHS, END_EXPR };
   Stmt* SubExprs[END_EXPR]; // Left/Middle/Right hand sides.
   SourceLocation BuiltinLoc, RParenLoc;
+  bool CondIsTrue;
 public:
   ChooseExpr(SourceLocation BLoc, Expr *cond, Expr *lhs, Expr *rhs,
              QualType t, ExprValueKind VK, ExprObjectKind OK,
-             SourceLocation RP, bool TypeDependent, bool ValueDependent)
+             SourceLocation RP, bool condIsTrue,
+             bool TypeDependent, bool ValueDependent)
     : Expr(ChooseExprClass, t, VK, OK, TypeDependent, ValueDependent,
            (cond->isInstantiationDependent() ||
             lhs->isInstantiationDependent() ||
@@ -3486,7 +3488,7 @@ public:
            (cond->containsUnexpandedParameterPack() ||
             lhs->containsUnexpandedParameterPack() ||
             rhs->containsUnexpandedParameterPack())),
-      BuiltinLoc(BLoc), RParenLoc(RP) {
+      BuiltinLoc(BLoc), RParenLoc(RP), CondIsTrue(condIsTrue) {
       SubExprs[COND] = cond;
       SubExprs[LHS] = lhs;
       SubExprs[RHS] = rhs;
@@ -3497,12 +3499,21 @@ public:
 
   /// isConditionTrue - Return whether the condition is true (i.e. not
   /// equal to zero).
-  bool isConditionTrue(const ASTContext &C) const;
+  bool isConditionTrue() const {
+    assert(!isConditionDependent() &&
+           "Dependent condition isn't true or false");
+    return CondIsTrue;
+  }
+  void setIsConditionTrue(bool isTrue) { CondIsTrue = isTrue; }
+
+  bool isConditionDependent() const {
+    return getCond()->isTypeDependent() || getCond()->isValueDependent();
+  }
 
   /// getChosenSubExpr - Return the subexpression chosen according to the
   /// condition.
-  Expr *getChosenSubExpr(const ASTContext &C) const {
-    return isConditionTrue(C) ? getLHS() : getRHS();
+  Expr *getChosenSubExpr() const {
+    return isConditionTrue() ? getLHS() : getRHS();
   }
 
   Expr *getCond() const { return cast<Expr>(SubExprs[COND]); }
index 37f7d267d97c41d6e380dac8460d03bcf9d7ef29..8473c096afeeadccf641f09e3640aa9201b23634 100644 (file)
@@ -1929,6 +1929,9 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
   case GenericSelectionExprClass:
     return cast<GenericSelectionExpr>(this)->getResultExpr()->
       isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+  case ChooseExprClass:
+    return cast<ChooseExpr>(this)->getChosenSubExpr()->
+      isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
   case UnaryOperatorClass: {
     const UnaryOperator *UO = cast<UnaryOperator>(this);
 
@@ -2295,6 +2298,12 @@ Expr* Expr::IgnoreParens() {
         continue;
       }
     }
+    if (ChooseExpr* P = dyn_cast<ChooseExpr>(E)) {
+      if (!P->isConditionDependent()) {
+        E = P->getChosenSubExpr();
+        continue;
+      }
+    }
     return E;
   }
 }
@@ -2304,26 +2313,11 @@ Expr* Expr::IgnoreParens() {
 Expr *Expr::IgnoreParenCasts() {
   Expr *E = this;
   while (true) {
-    if (ParenExpr* P = dyn_cast<ParenExpr>(E)) {
-      E = P->getSubExpr();
-      continue;
-    }
+    E = E->IgnoreParens();
     if (CastExpr *P = dyn_cast<CastExpr>(E)) {
       E = P->getSubExpr();
       continue;
     }
-    if (UnaryOperator* P = dyn_cast<UnaryOperator>(E)) {
-      if (P->getOpcode() == UO_Extension) {
-        E = P->getSubExpr();
-        continue;
-      }
-    }
-    if (GenericSelectionExpr* P = dyn_cast<GenericSelectionExpr>(E)) {
-      if (!P->isResultDependent()) {
-        E = P->getResultExpr();
-        continue;
-      }
-    }
     if (MaterializeTemporaryExpr *Materialize 
                                       = dyn_cast<MaterializeTemporaryExpr>(E)) {
       E = Materialize->GetTemporaryExpr();
@@ -2345,24 +2339,12 @@ Expr *Expr::IgnoreParenCasts() {
 Expr *Expr::IgnoreParenLValueCasts() {
   Expr *E = this;
   while (true) {
-    if (ParenExpr *P = dyn_cast<ParenExpr>(E)) {
-      E = P->getSubExpr();
-      continue;
-    } else if (CastExpr *P = dyn_cast<CastExpr>(E)) {
+    E = E->IgnoreParens();
+    if (CastExpr *P = dyn_cast<CastExpr>(E)) {
       if (P->getCastKind() == CK_LValueToRValue) {
         E = P->getSubExpr();
         continue;
       }
-    } else if (UnaryOperator* P = dyn_cast<UnaryOperator>(E)) {
-      if (P->getOpcode() == UO_Extension) {
-        E = P->getSubExpr();
-        continue;
-      }
-    } else if (GenericSelectionExpr* P = dyn_cast<GenericSelectionExpr>(E)) {
-      if (!P->isResultDependent()) {
-        E = P->getResultExpr();
-        continue;
-      }
     } else if (MaterializeTemporaryExpr *Materialize 
                                       = dyn_cast<MaterializeTemporaryExpr>(E)) {
       E = Materialize->GetTemporaryExpr();
@@ -2380,10 +2362,7 @@ Expr *Expr::IgnoreParenLValueCasts() {
 Expr *Expr::ignoreParenBaseCasts() {
   Expr *E = this;
   while (true) {
-    if (ParenExpr *P = dyn_cast<ParenExpr>(E)) {
-      E = P->getSubExpr();
-      continue;
-    }
+    E = E->IgnoreParens();
     if (CastExpr *CE = dyn_cast<CastExpr>(E)) {
       if (CE->getCastKind() == CK_DerivedToBase ||
           CE->getCastKind() == CK_UncheckedDerivedToBase ||
@@ -2400,26 +2379,11 @@ Expr *Expr::ignoreParenBaseCasts() {
 Expr *Expr::IgnoreParenImpCasts() {
   Expr *E = this;
   while (true) {
-    if (ParenExpr *P = dyn_cast<ParenExpr>(E)) {
-      E = P->getSubExpr();
-      continue;
-    }
+    E = E->IgnoreParens();
     if (ImplicitCastExpr *P = dyn_cast<ImplicitCastExpr>(E)) {
       E = P->getSubExpr();
       continue;
     }
-    if (UnaryOperator* P = dyn_cast<UnaryOperator>(E)) {
-      if (P->getOpcode() == UO_Extension) {
-        E = P->getSubExpr();
-        continue;
-      }
-    }
-    if (GenericSelectionExpr* P = dyn_cast<GenericSelectionExpr>(E)) {
-      if (!P->isResultDependent()) {
-        E = P->getResultExpr();
-        continue;
-      }
-    }
     if (MaterializeTemporaryExpr *Materialize 
                                       = dyn_cast<MaterializeTemporaryExpr>(E)) {
       E = Materialize->GetTemporaryExpr();
@@ -2448,10 +2412,7 @@ Expr *Expr::IgnoreConversionOperator() {
 Expr *Expr::IgnoreParenNoopCasts(ASTContext &Ctx) {
   Expr *E = this;
   while (true) {
-    if (ParenExpr *P = dyn_cast<ParenExpr>(E)) {
-      E = P->getSubExpr();
-      continue;
-    }
+    E = E->IgnoreParens();
 
     if (CastExpr *P = dyn_cast<CastExpr>(E)) {
       // We ignore integer <-> casts that are of the same width, ptr<->ptr and
@@ -2473,20 +2434,6 @@ Expr *Expr::IgnoreParenNoopCasts(ASTContext &Ctx) {
       }
     }
 
-    if (UnaryOperator* P = dyn_cast<UnaryOperator>(E)) {
-      if (P->getOpcode() == UO_Extension) {
-        E = P->getSubExpr();
-        continue;
-      }
-    }
-
-    if (GenericSelectionExpr* P = dyn_cast<GenericSelectionExpr>(E)) {
-      if (!P->isResultDependent()) {
-        E = P->getResultExpr();
-        continue;
-      }
-    }
-
     if (SubstNonTypeTemplateParmExpr *NTTP
                                   = dyn_cast<SubstNonTypeTemplateParmExpr>(E)) {
       E = NTTP->getReplacement();
@@ -2728,7 +2675,9 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const {
     return cast<GenericSelectionExpr>(this)->getResultExpr()
       ->isConstantInitializer(Ctx, IsForRef);
   case ChooseExprClass:
-    return cast<ChooseExpr>(this)->getChosenSubExpr(Ctx)
+    if (cast<ChooseExpr>(this)->isConditionDependent())
+      return false;
+    return cast<ChooseExpr>(this)->getChosenSubExpr()
       ->isConstantInitializer(Ctx, IsForRef);
   case UnaryOperatorClass: {
     const UnaryOperator* Exp = cast<UnaryOperator>(this);
@@ -2887,7 +2836,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx) const {
         HasSideEffects(Ctx);
 
   case ChooseExprClass:
-    return cast<ChooseExpr>(this)->getChosenSubExpr(Ctx)->HasSideEffects(Ctx);
+    return cast<ChooseExpr>(this)->getChosenSubExpr()->HasSideEffects(Ctx);
 
   case CXXDefaultArgExprClass:
     return cast<CXXDefaultArgExpr>(this)->getExpr()->HasSideEffects(Ctx);
@@ -3082,7 +3031,13 @@ Expr::isNullPointerConstant(ASTContext &Ctx,
     return PE->getSubExpr()->isNullPointerConstant(Ctx, NPC);
   } else if (const GenericSelectionExpr *GE =
                dyn_cast<GenericSelectionExpr>(this)) {
+    if (GE->isResultDependent())
+      return NPCK_NotNull;
     return GE->getResultExpr()->isNullPointerConstant(Ctx, NPC);
+  } else if (const ChooseExpr *CE = dyn_cast<ChooseExpr>(this)) {
+    if (CE->isConditionDependent())
+      return NPCK_NotNull;
+    return CE->getChosenSubExpr()->isNullPointerConstant(Ctx, NPC);
   } else if (const CXXDefaultArgExpr *DefaultArg
                = dyn_cast<CXXDefaultArgExpr>(this)) {
     // See through default argument expressions.
@@ -3567,10 +3522,6 @@ StringRef ObjCBridgedCastExpr::getBridgeKindName() const {
   llvm_unreachable("Invalid BridgeKind!");
 }
 
-bool ChooseExpr::isConditionTrue(const ASTContext &C) const {
-  return getCond()->EvaluateKnownConstInt(C) != 0;
-}
-
 ShuffleVectorExpr::ShuffleVectorExpr(ASTContext &C, ArrayRef<Expr*> args,
                                      QualType Type, SourceLocation BLoc,
                                      SourceLocation RP) 
index 72f17669ed17d1a4cc18e40a6955105c78f07036..1c91af7d47fdaa494b30f677c84869a482639bf1 100644 (file)
@@ -286,7 +286,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
 
     // __builtin_choose_expr is equivalent to the chosen expression.
   case Expr::ChooseExprClass:
-    return ClassifyInternal(Ctx, cast<ChooseExpr>(E)->getChosenSubExpr(Ctx));
+    return ClassifyInternal(Ctx, cast<ChooseExpr>(E)->getChosenSubExpr());
 
     // Extended vector element access is an lvalue unless there are duplicates
     // in the shuffle expression.
index 28473f0bdbd8969a64300c4cc2bd1a0d402ed778..ddfb64c44d54c4adad824d8b041943f279ab4537 100644 (file)
@@ -3585,7 +3585,7 @@ public:
   RetTy VisitUnaryPlus(const UnaryOperator *E)
     { return StmtVisitorTy::Visit(E->getSubExpr()); }
   RetTy VisitChooseExpr(const ChooseExpr *E)
-    { return StmtVisitorTy::Visit(E->getChosenSubExpr(Info.Ctx)); }
+    { return StmtVisitorTy::Visit(E->getChosenSubExpr()); }
   RetTy VisitGenericSelectionExpr(const GenericSelectionExpr *E)
     { return StmtVisitorTy::Visit(E->getResultExpr()); }
   RetTy VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E)
@@ -8263,7 +8263,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) {
   case Expr::CXXDefaultInitExprClass:
     return CheckICE(cast<CXXDefaultInitExpr>(E)->getExpr(), Ctx);
   case Expr::ChooseExprClass: {
-    return CheckICE(cast<ChooseExpr>(E)->getChosenSubExpr(Ctx), Ctx);
+    return CheckICE(cast<ChooseExpr>(E)->getChosenSubExpr(), Ctx);
   }
   }
 
index b283174514e980ff156ef4d26198bd0f6028b834..d6ff93f5fb78e22d4624ad1ad932b63b0c61d2a8 100644 (file)
@@ -851,7 +851,7 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
   case Expr::BinaryConditionalOperatorClass:
     return EmitConditionalOperatorLValue(cast<BinaryConditionalOperator>(E));
   case Expr::ChooseExprClass:
-    return EmitLValue(cast<ChooseExpr>(E)->getChosenSubExpr(getContext()));
+    return EmitLValue(cast<ChooseExpr>(E)->getChosenSubExpr());
   case Expr::OpaqueValueExprClass:
     return EmitOpaqueValueLValue(cast<OpaqueValueExpr>(E));
   case Expr::SubstNonTypeTemplateParmExprClass:
index 902bf1605a2e68d1172fb303d538a47ef1fcf697..8268c86c9a158d6d67f2a6789afc49d45a529148 100644 (file)
@@ -920,7 +920,7 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
 }
 
 void AggExprEmitter::VisitChooseExpr(const ChooseExpr *CE) {
-  Visit(CE->getChosenSubExpr(CGF.getContext()));
+  Visit(CE->getChosenSubExpr());
 }
 
 void AggExprEmitter::VisitVAArgExpr(VAArgExpr *VE) {
index 5ba13175d539473366e0fea05d5c2616f1bc15bc..fcff8e9439c5d36939a0b927ce45a67bdfeafba9 100644 (file)
@@ -778,7 +778,7 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
 }
 
 ComplexPairTy ComplexExprEmitter::VisitChooseExpr(ChooseExpr *E) {
-  return Visit(E->getChosenSubExpr(CGF.getContext()));
+  return Visit(E->getChosenSubExpr());
 }
 
 ComplexPairTy ComplexExprEmitter::VisitInitListExpr(InitListExpr *E) {
index 4a6f90a30e73a11ce4c0771bd7500fcc5a7eb050..431ee3c47ffbcd704a6b48e3fae347b16a5d7695 100644 (file)
@@ -610,7 +610,7 @@ public:
   }
 
   llvm::Constant *VisitChooseExpr(ChooseExpr *CE) {
-    return Visit(CE->getChosenSubExpr(CGM.getContext()));
+    return Visit(CE->getChosenSubExpr());
   }
 
   llvm::Constant *VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
index bfd880a7e36e1b7cfad676e0f54f28f1a552c283..7d98764548f96f4aa161f9c8bd5c1884b238ff19 100644 (file)
@@ -3101,7 +3101,7 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
 }
 
 Value *ScalarExprEmitter::VisitChooseExpr(ChooseExpr *E) {
-  return Visit(E->getChosenSubExpr(CGF.getContext()));
+  return Visit(E->getChosenSubExpr());
 }
 
 Value *ScalarExprEmitter::VisitVAArgExpr(VAArgExpr *VE) {
index 0385c7783e33fda6d85717e9bcdc60bb34167fcb..0d662c8f393bf11e93a4a412f866a634a26371fa 100644 (file)
@@ -1025,7 +1025,7 @@ CanThrowResult Sema::canThrow(const Expr *E) {
   case Expr::ChooseExprClass:
     if (E->isTypeDependent() || E->isValueDependent())
       return CT_Dependent;
-    return canThrow(cast<ChooseExpr>(E)->getChosenSubExpr(Context));
+    return canThrow(cast<ChooseExpr>(E)->getChosenSubExpr());
 
   case Expr::GenericSelectionExprClass:
     if (cast<GenericSelectionExpr>(E)->isResultDependent())
index f397c5bb2ef7e337425e69e39dca1f782ab4fc38..2c933147f44816cded0719f2813b23f4000792b5 100644 (file)
@@ -9868,6 +9868,7 @@ ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc,
   ExprObjectKind OK = OK_Ordinary;
   QualType resType;
   bool ValueDependent = false;
+  bool CondIsTrue = false;
   if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) {
     resType = Context.DependentTy;
     ValueDependent = true;
@@ -9880,9 +9881,10 @@ ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc,
     if (CondICE.isInvalid())
       return ExprError();
     CondExpr = CondICE.take();
+    CondIsTrue = condEval.getZExtValue();
 
     // If the condition is > zero, then the AST type is the same as the LSHExpr.
-    Expr *ActiveExpr = condEval.getZExtValue() ? LHSExpr : RHSExpr;
+    Expr *ActiveExpr = CondIsTrue ? LHSExpr : RHSExpr;
 
     resType = ActiveExpr->getType();
     ValueDependent = ActiveExpr->isValueDependent();
@@ -9891,7 +9893,7 @@ ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc,
   }
 
   return Owned(new (Context) ChooseExpr(BuiltinLoc, CondExpr, LHSExpr, RHSExpr,
-                                        resType, VK, OK, RPLoc,
+                                        resType, VK, OK, RPLoc, CondIsTrue,
                                         resType->isDependentType(),
                                         ValueDependent));
 }
index a10612a8b07bb9df5091882d65b81fee5034415d..106250fe0f5286e9bfc649fd5a1bd3171818095d 100644 (file)
@@ -101,6 +101,25 @@ namespace {
                                                     resultIndex);
       }
 
+      if (ChooseExpr *ce = dyn_cast<ChooseExpr>(e)) {
+        assert(!ce->isConditionDependent());
+
+        Expr *LHS = ce->getLHS(), *RHS = ce->getRHS();
+        Expr *&rebuiltExpr = ce->isConditionTrue() ? LHS : RHS;
+        rebuiltExpr = rebuild(rebuiltExpr);
+
+        return new (S.Context) ChooseExpr(ce->getBuiltinLoc(),
+                                          ce->getCond(),
+                                          LHS, RHS,
+                                          rebuiltExpr->getType(),
+                                          rebuiltExpr->getValueKind(),
+                                          rebuiltExpr->getObjectKind(),
+                                          ce->getRParenLoc(),
+                                          ce->isConditionTrue(),
+                                          rebuiltExpr->isTypeDependent(),
+                                          rebuiltExpr->isValueDependent());
+      }
+
       llvm_unreachable("bad expression to rebuild!");
     }
   };
index 113b12ff59bc4ff1543e2da8a21ec3c6fcc86e8d..ed07b0532f13c6663621bbb15efb27343568c5ee 100644 (file)
@@ -835,6 +835,7 @@ void ASTStmtReader::VisitChooseExpr(ChooseExpr *E) {
   E->setRHS(Reader.ReadSubExpr());
   E->setBuiltinLoc(ReadSourceLocation(Record, Idx));
   E->setRParenLoc(ReadSourceLocation(Record, Idx));
+  E->setIsConditionTrue(Record[Idx++]);
 }
 
 void ASTStmtReader::VisitGNUNullExpr(GNUNullExpr *E) {
index 8d47158f941d8bab9f8a6b4b24badeb4ac9a741e..d8b852136c988954ee0991cae7d427054e62212e 100644 (file)
@@ -773,6 +773,7 @@ void ASTStmtWriter::VisitChooseExpr(ChooseExpr *E) {
   Writer.AddStmt(E->getRHS());
   Writer.AddSourceLocation(E->getBuiltinLoc(), Record);
   Writer.AddSourceLocation(E->getRParenLoc(), Record);
+  Record.push_back(E->isConditionDependent() ? false : E->isConditionTrue());
   Code = serialization::EXPR_CHOOSE;
 }
 
index 271ba4702c579960f936d97c5c4c4782f393daa9..d756c62956825ca2da60cae6c5d3ef42ca2865cf 100644 (file)
@@ -678,19 +678,8 @@ bool IdempotentOperationChecker::CanVary(const Expr *Ex,
     return CanVary(B->getRHS(), AC)
         || CanVary(B->getLHS(), AC);
    }
-  case Stmt::UnaryOperatorClass: {
-    const UnaryOperator *U = cast<const UnaryOperator>(Ex);
-    // Handle trivial case first
-    switch (U->getOpcode()) {
-    case UO_Extension:
-      return false;
-    default:
-      return CanVary(U->getSubExpr(), AC);
-    }
-  }
-  case Stmt::ChooseExprClass:
-    return CanVary(cast<const ChooseExpr>(Ex)->getChosenSubExpr(
-        AC->getASTContext()), AC);
+  case Stmt::UnaryOperatorClass:
+    return CanVary(cast<UnaryOperator>(Ex)->getSubExpr(), AC);
   case Stmt::ConditionalOperatorClass:
   case Stmt::BinaryConditionalOperatorClass:
     return CanVary(cast<AbstractConditionalOperator>(Ex)->getCond(), AC);
diff --git a/test/SemaObjC/property-choose-expr.m b/test/SemaObjC/property-choose-expr.m
new file mode 100644 (file)
index 0000000..71265e5
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
+// expected-no-diagnostics
+
+@interface NSArray
+-(int)count;
+@end
+
+// <rdar://problem/14438917>
+char* f(NSArray *array) {
+    return _Generic(__builtin_choose_expr(__builtin_types_compatible_p(__typeof__(array.count), void), 0.f, array.count),
+                    unsigned int:"uint",
+                    float:"void",
+                    default: "ignored");
+}