[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-04-07 Thread John McCall via Phabricator via cfe-commits
rjmccall closed this revision.
rjmccall added a comment.

Committed as r329508.


Repository:
  rC Clang

https://reviews.llvm.org/D44580



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


[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-04-04 Thread Dustin L. Howett via Phabricator via cfe-commits
DHowett-MSFT added a comment.

Thank you for the review! I don't have the means to check this in myself.


Repository:
  rC Clang

https://reviews.llvm.org/D44580



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


[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-04-03 Thread John McCall via Phabricator via cfe-commits
rjmccall accepted this revision.
rjmccall added a comment.
This revision is now accepted and ready to land.

Okay, LGTM with the reduced set of changes to the functionality.


Repository:
  rC Clang

https://reviews.llvm.org/D44580



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


[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-03-19 Thread Dustin L. Howett via Phabricator via cfe-commits
DHowett-MSFT updated this revision to Diff 138996.
DHowett-MSFT added a comment.

Ran `clang-format` over changed lines. Reuploaded diff with full context.


Repository:
  rC Clang

https://reviews.llvm.org/D44580

Files:
  lib/Sema/SemaExpr.cpp
  test/SemaObjC/block-compare.mm


Index: test/SemaObjC/block-compare.mm
===
--- /dev/null
+++ test/SemaObjC/block-compare.mm
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -S -o - -triple i686-windows -verify -fblocks \
+// RUN: -Wno-unused-comparison %s
+
+#pragma clang diagnostic ignored "-Wunused-comparison"
+
+#define nil ((id)nullptr)
+
+@protocol NSObject
+@end
+
+@protocol NSCopying
+@end
+
+@protocol OtherProtocol
+@end
+
+__attribute__((objc_root_class))
+@interface NSObject 
+@end
+
+__attribute__((objc_root_class))
+@interface Test
+@end
+
+int main() {
+  void (^block)() = ^{};
+  NSObject *object;
+  id qualifiedId;
+
+  id poorlyQualified1;
+  Test *objectOfWrongType;
+
+  block == nil;
+  block == object;
+  block == qualifiedId;
+
+  nil == block;
+  object == block;
+  qualifiedId == block;
+
+  // these are still not valid: blocks must be compared with id, NSObject*, or 
a protocol-qualified id
+  // conforming to NSCopying or NSObject.
+
+  block == poorlyQualified1; // expected-error {{invalid operands to binary 
expression ('void (^)()' and 'id')}}
+  block == objectOfWrongType; // expected-error {{invalid operands to binary 
expression ('void (^)()' and 'Test *')}}
+
+  poorlyQualified1 == block; // expected-error {{invalid operands to binary 
expression ('id' and 'void (^)()')}}
+  objectOfWrongType == block; // expected-error {{invalid operands to binary 
expression ('Test *' and 'void (^)()')}}
+
+  return 0;
+}
Index: lib/Sema/SemaExpr.cpp
===
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -10028,6 +10028,19 @@
 RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
   return ResultTy;
 }
+
+if (!IsRelational && LHSType->isBlockPointerType() &&
+RHSType->isBlockCompatibleObjCPointerType(Context)) {
+  LHS = ImpCastExprToType(LHS.get(), RHSType,
+  CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+} else if (!IsRelational &&
+   LHSType->isBlockCompatibleObjCPointerType(Context) &&
+   RHSType->isBlockPointerType()) {
+  RHS = ImpCastExprToType(RHS.get(), LHSType,
+  CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+}
   }
   if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) ||
   (LHSType->isIntegerType() && RHSType->isAnyPointerType())) {


Index: test/SemaObjC/block-compare.mm
===
--- /dev/null
+++ test/SemaObjC/block-compare.mm
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -S -o - -triple i686-windows -verify -fblocks \
+// RUN: -Wno-unused-comparison %s
+
+#pragma clang diagnostic ignored "-Wunused-comparison"
+
+#define nil ((id)nullptr)
+
+@protocol NSObject
+@end
+
+@protocol NSCopying
+@end
+
+@protocol OtherProtocol
+@end
+
+__attribute__((objc_root_class))
+@interface NSObject 
+@end
+
+__attribute__((objc_root_class))
+@interface Test
+@end
+
+int main() {
+  void (^block)() = ^{};
+  NSObject *object;
+  id qualifiedId;
+
+  id poorlyQualified1;
+  Test *objectOfWrongType;
+
+  block == nil;
+  block == object;
+  block == qualifiedId;
+
+  nil == block;
+  object == block;
+  qualifiedId == block;
+
+  // these are still not valid: blocks must be compared with id, NSObject*, or a protocol-qualified id
+  // conforming to NSCopying or NSObject.
+
+  block == poorlyQualified1; // expected-error {{invalid operands to binary expression ('void (^)()' and 'id')}}
+  block == objectOfWrongType; // expected-error {{invalid operands to binary expression ('void (^)()' and 'Test *')}}
+
+  poorlyQualified1 == block; // expected-error {{invalid operands to binary expression ('id' and 'void (^)()')}}
+  objectOfWrongType == block; // expected-error {{invalid operands to binary expression ('Test *' and 'void (^)()')}}
+
+  return 0;
+}
Index: lib/Sema/SemaExpr.cpp
===
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -10028,6 +10028,19 @@
 RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
   return ResultTy;
 }
+
+if (!IsRelational && LHSType->isBlockPointerType() &&
+RHSType->isBlockCompatibleObjCPointerType(Context)) {
+  LHS = ImpCastExprToType(LHS.get(), RHSType,
+  CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+} else if (!IsRelational &&
+   LHSType->isBlockCompatibleObjCPointerType(Context) &&
+   RHSType->isBlockPointerType()) {
+  RHS = ImpCastExprToType(RHS.get(), LHSType,
+  CK_B

[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-03-18 Thread Dustin L. Howett via Phabricator via cfe-commits
DHowett-MSFT updated this revision to Diff 138868.
DHowett-MSFT marked an inline comment as done.
DHowett-MSFT added a comment.

Backed out changes to block type assignment.


Repository:
  rC Clang

https://reviews.llvm.org/D44580

Files:
  lib/Sema/SemaExpr.cpp
  test/SemaObjC/block-compare.mm


Index: test/SemaObjC/block-compare.mm
===
--- /dev/null
+++ test/SemaObjC/block-compare.mm
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -S -o - -triple i686-windows -verify -fblocks \
+// RUN: -Wno-unused-comparison %s
+
+#pragma clang diagnostic ignored "-Wunused-comparison"
+
+#define nil ((id)nullptr)
+
+@protocol NSObject
+@end
+
+@protocol NSCopying
+@end
+
+@protocol OtherProtocol
+@end
+
+__attribute__((objc_root_class))
+@interface NSObject 
+@end
+
+__attribute__((objc_root_class))
+@interface Test
+@end
+
+int main() {
+  void (^block)() = ^{};
+  NSObject *object;
+  id qualifiedId;
+
+  id poorlyQualified1;
+  Test *objectOfWrongType;
+
+  block == nil;
+  block == object;
+  block == qualifiedId;
+
+  nil == block;
+  object == block;
+  qualifiedId == block;
+
+  // these are still not valid: blocks must be compared with id, NSObject*, or 
a protocol-qualified id
+  // conforming to NSCopying or NSObject.
+
+  block == poorlyQualified1; // expected-error {{invalid operands to binary 
expression ('void (^)()' and 'id')}}
+  block == objectOfWrongType; // expected-error {{invalid operands to binary 
expression ('void (^)()' and 'Test *')}}
+
+  poorlyQualified1 == block; // expected-error {{invalid operands to binary 
expression ('id' and 'void (^)()')}}
+  objectOfWrongType == block; // expected-error {{invalid operands to binary 
expression ('Test *' and 'void (^)()')}}
+
+  return 0;
+}
Index: lib/Sema/SemaExpr.cpp
===
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -10028,6 +10028,18 @@
 RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
   return ResultTy;
 }
+
+if(!IsRelational &&
+   LHSType->isBlockPointerType() &&
+   RHSType->isBlockCompatibleObjCPointerType(Context)) {
+  LHS = ImpCastExprToType(LHS.get(), RHSType, 
CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+} else if(!IsRelational &&
+  LHSType->isBlockCompatibleObjCPointerType(Context) &&
+  RHSType->isBlockPointerType()) {
+  RHS = ImpCastExprToType(RHS.get(), LHSType, 
CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+}
   }
   if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) ||
   (LHSType->isIntegerType() && RHSType->isAnyPointerType())) {


Index: test/SemaObjC/block-compare.mm
===
--- /dev/null
+++ test/SemaObjC/block-compare.mm
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -S -o - -triple i686-windows -verify -fblocks \
+// RUN: -Wno-unused-comparison %s
+
+#pragma clang diagnostic ignored "-Wunused-comparison"
+
+#define nil ((id)nullptr)
+
+@protocol NSObject
+@end
+
+@protocol NSCopying
+@end
+
+@protocol OtherProtocol
+@end
+
+__attribute__((objc_root_class))
+@interface NSObject 
+@end
+
+__attribute__((objc_root_class))
+@interface Test
+@end
+
+int main() {
+  void (^block)() = ^{};
+  NSObject *object;
+  id qualifiedId;
+
+  id poorlyQualified1;
+  Test *objectOfWrongType;
+
+  block == nil;
+  block == object;
+  block == qualifiedId;
+
+  nil == block;
+  object == block;
+  qualifiedId == block;
+
+  // these are still not valid: blocks must be compared with id, NSObject*, or a protocol-qualified id
+  // conforming to NSCopying or NSObject.
+
+  block == poorlyQualified1; // expected-error {{invalid operands to binary expression ('void (^)()' and 'id')}}
+  block == objectOfWrongType; // expected-error {{invalid operands to binary expression ('void (^)()' and 'Test *')}}
+
+  poorlyQualified1 == block; // expected-error {{invalid operands to binary expression ('id' and 'void (^)()')}}
+  objectOfWrongType == block; // expected-error {{invalid operands to binary expression ('Test *' and 'void (^)()')}}
+
+  return 0;
+}
Index: lib/Sema/SemaExpr.cpp
===
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -10028,6 +10028,18 @@
 RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
   return ResultTy;
 }
+
+if(!IsRelational &&
+   LHSType->isBlockPointerType() &&
+   RHSType->isBlockCompatibleObjCPointerType(Context)) {
+  LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+} else if(!IsRelational &&
+  LHSType->isBlockCompatibleObjCPointerType(Context) &&
+  RHSType->isBlockPointerType()) {
+  RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+}
   }
   if ((LHSType->isAnyPointerTy

[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-03-18 Thread Dustin L. Howett via Phabricator via cfe-commits
DHowett-MSFT planned changes to this revision.
DHowett-MSFT marked an inline comment as done.
DHowett-MSFT added inline comments.



Comment at: lib/Sema/SemaExpr.cpp:7749
+// id (or strictly compatible object type) -> T^
+if (getLangOpts().ObjC1 && 
RHSType->isBlockCompatibleObjCPointerType(Context)) {
   Kind = CK_AnyPointerToBlockPointerCast;

theraven wrote:
> Do we want to allow implicit casts for all object types to block types for 
> assignment, or only for null pointers?  We definitely want to allow `nil` to 
> be assigned to a block type, but I would lean slightly to requiring an 
> implicit cast.
> 
> Ideally, I think we'd allow this but warn, because casting from an arbitrary 
> ObjC type to a block incorrectly can cause exciting security vulnerabilities 
> if it's done incorrectly and we should encourage people to check these casts 
> (`nil` is always safe though - as long as somewhere else checks the 
> nullability attributes).
I don't actually have a compelling use case for widening assignability here. 
I'm dropping this part of the patch.

I do think it should be valid, perhaps with a warning. It feels incorrect for 
there to be valid comparison cases that are not valid assignment cases (here), 
but that position doesn't hold up under scrutiny.


Repository:
  rC Clang

https://reviews.llvm.org/D44580



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


[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-03-17 Thread David Chisnall via Phabricator via cfe-commits
theraven added a comment.

We seem to be missing tests for the assignment part of this patch.




Comment at: lib/Sema/SemaExpr.cpp:7749
+// id (or strictly compatible object type) -> T^
+if (getLangOpts().ObjC1 && 
RHSType->isBlockCompatibleObjCPointerType(Context)) {
   Kind = CK_AnyPointerToBlockPointerCast;

Do we want to allow implicit casts for all object types to block types for 
assignment, or only for null pointers?  We definitely want to allow `nil` to be 
assigned to a block type, but I would lean slightly to requiring an implicit 
cast.

Ideally, I think we'd allow this but warn, because casting from an arbitrary 
ObjC type to a block incorrectly can cause exciting security vulnerabilities if 
it's done incorrectly and we should encourage people to check these casts 
(`nil` is always safe though - as long as somewhere else checks the nullability 
attributes).


Repository:
  rC Clang

https://reviews.llvm.org/D44580



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


[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-03-16 Thread Dustin L. Howett via Phabricator via cfe-commits
DHowett-MSFT updated this revision to Diff 138759.
DHowett-MSFT removed a subscriber: lebedev.ri.
DHowett-MSFT added a comment.

Moved files around slightly.


Repository:
  rC Clang

https://reviews.llvm.org/D44580

Files:
  lib/Sema/SemaExpr.cpp
  test/SemaObjC/block-compare.mm


Index: test/SemaObjC/block-compare.mm
===
--- /dev/null
+++ test/SemaObjC/block-compare.mm
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -S -o - -triple i686-windows -verify -fblocks \
+// RUN: -Wno-unused-comparison %s
+
+#pragma clang diagnostic ignored "-Wunused-comparison"
+
+#define nil ((id)nullptr)
+
+@protocol NSObject
+@end
+
+@protocol NSCopying
+@end
+
+@protocol OtherProtocol
+@end
+
+__attribute__((objc_root_class))
+@interface NSObject 
+@end
+
+__attribute__((objc_root_class))
+@interface Test
+@end
+
+int main() {
+  void (^block)() = ^{};
+  NSObject *object;
+  id qualifiedId;
+
+  id poorlyQualified1;
+  Test *objectOfWrongType;
+
+  block == nil;
+  block == object;
+  block == qualifiedId;
+
+  nil == block;
+  object == block;
+  qualifiedId == block;
+
+  // these are still not valid: blocks must be compared with id, NSObject*, or 
a protocol-qualified id
+  // conforming to NSCopying or NSObject.
+
+  block == poorlyQualified1; // expected-error {{invalid operands to binary 
expression ('void (^)()' and 'id')}}
+  block == objectOfWrongType; // expected-error {{invalid operands to binary 
expression ('void (^)()' and 'Test *')}}
+
+  poorlyQualified1 == block; // expected-error {{invalid operands to binary 
expression ('id' and 'void (^)()')}}
+  objectOfWrongType == block; // expected-error {{invalid operands to binary 
expression ('Test *' and 'void (^)()')}}
+
+  return 0;
+}
Index: lib/Sema/SemaExpr.cpp
===
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -7745,8 +7745,8 @@
   return IntToBlockPointer;
 }
 
-// id -> T^
-if (getLangOpts().ObjC1 && RHSType->isObjCIdType()) {
+// id (or strictly compatible object type) -> T^
+if (getLangOpts().ObjC1 && 
RHSType->isBlockCompatibleObjCPointerType(Context)) {
   Kind = CK_AnyPointerToBlockPointerCast;
   return Compatible;
 }
@@ -10028,6 +10028,18 @@
 RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
   return ResultTy;
 }
+
+if(!IsRelational &&
+   LHSType->isBlockPointerType() &&
+   RHSType->isBlockCompatibleObjCPointerType(Context)) {
+  LHS = ImpCastExprToType(LHS.get(), RHSType, 
CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+} else if(!IsRelational &&
+  LHSType->isBlockCompatibleObjCPointerType(Context) &&
+  RHSType->isBlockPointerType()) {
+  RHS = ImpCastExprToType(RHS.get(), LHSType, 
CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+}
   }
   if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) ||
   (LHSType->isIntegerType() && RHSType->isAnyPointerType())) {


Index: test/SemaObjC/block-compare.mm
===
--- /dev/null
+++ test/SemaObjC/block-compare.mm
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -S -o - -triple i686-windows -verify -fblocks \
+// RUN: -Wno-unused-comparison %s
+
+#pragma clang diagnostic ignored "-Wunused-comparison"
+
+#define nil ((id)nullptr)
+
+@protocol NSObject
+@end
+
+@protocol NSCopying
+@end
+
+@protocol OtherProtocol
+@end
+
+__attribute__((objc_root_class))
+@interface NSObject 
+@end
+
+__attribute__((objc_root_class))
+@interface Test
+@end
+
+int main() {
+  void (^block)() = ^{};
+  NSObject *object;
+  id qualifiedId;
+
+  id poorlyQualified1;
+  Test *objectOfWrongType;
+
+  block == nil;
+  block == object;
+  block == qualifiedId;
+
+  nil == block;
+  object == block;
+  qualifiedId == block;
+
+  // these are still not valid: blocks must be compared with id, NSObject*, or a protocol-qualified id
+  // conforming to NSCopying or NSObject.
+
+  block == poorlyQualified1; // expected-error {{invalid operands to binary expression ('void (^)()' and 'id')}}
+  block == objectOfWrongType; // expected-error {{invalid operands to binary expression ('void (^)()' and 'Test *')}}
+
+  poorlyQualified1 == block; // expected-error {{invalid operands to binary expression ('id' and 'void (^)()')}}
+  objectOfWrongType == block; // expected-error {{invalid operands to binary expression ('Test *' and 'void (^)()')}}
+
+  return 0;
+}
Index: lib/Sema/SemaExpr.cpp
===
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -7745,8 +7745,8 @@
   return IntToBlockPointer;
 }
 
-// id -> T^
-if (getLangOpts().ObjC1 && RHSType->isObjCIdType()) {
+// id (or strictly compatible object type) -> T^
+if (getLangOpts().ObjC1 && RHSType->isBlockCompatibleObjCPointerType(Context)) {
   Kind = CK_AnyPointerToBlockPoint

[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-03-16 Thread Dustin L. Howett via Phabricator via cfe-commits
DHowett-MSFT added a subscriber: lebedev.ri.
DHowett-MSFT added a comment.

In https://reviews.llvm.org/D44580#1040540, @lebedev.ri wrote:

> Why not `test/SemaObjC/block_compare.mm` ?


I wasn't are that it existed. It may even be a good fit for 
`test/SemaObjC/block-type-safety.m`, which already exists.


Repository:
  rC Clang

https://reviews.llvm.org/D44580



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


[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-03-16 Thread Roman Lebedev via Phabricator via cfe-commits
lebedev.ri added a comment.

Why not `test/SemaObjC/block_compare.mm` ?


Repository:
  rC Clang

https://reviews.llvm.org/D44580



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


[PATCH] D44580: Sema: allow comparison between blocks & block-compatible objc types

2018-03-16 Thread Dustin L. Howett via Phabricator via cfe-commits
DHowett-MSFT created this revision.
DHowett-MSFT added reviewers: theraven, rjmccall.
DHowett-MSFT added a project: clang.
Herald added a subscriber: cfe-commits.

This commit makes valid the following code:

  // objective-c++
  #define nil ((id)nullptr)
  ...
  void (^f)() = ^{};
  if (f == nil) {
  }
  …

Where it would previously fail with the error `invalid operands to binary 
expression ('void (^)()' and 'id')`.

Comparisons are now allowed between block types and `id`, `id`, 
`id`, and `NSObject*`. No other comparisons are changed.


Repository:
  rC Clang

https://reviews.llvm.org/D44580

Files:
  lib/Sema/SemaExpr.cpp
  test/Sema/objc_block_compare.mm


Index: test/Sema/objc_block_compare.mm
===
--- /dev/null
+++ test/Sema/objc_block_compare.mm
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -S -o - -triple i686-windows -verify -fblocks \
+// RUN: -Wno-unused-comparison %s
+
+#pragma clang diagnostic ignored "-Wunused-comparison"
+
+#define nil ((id)nullptr)
+
+@protocol NSObject
+@end
+
+@protocol NSCopying
+@end
+
+@protocol OtherProtocol
+@end
+
+__attribute__((objc_root_class))
+@interface NSObject 
+@end
+
+__attribute__((objc_root_class))
+@interface Test
+@end
+
+int main() {
+  void (^block)() = ^{};
+  NSObject *object;
+  id qualifiedId;
+
+  id poorlyQualified1;
+  Test *objectOfWrongType;
+
+  block == nil;
+  block == object;
+  block == qualifiedId;
+
+  nil == block;
+  object == block;
+  qualifiedId == block;
+
+  // these are still not valid: blocks must be compared with id, NSObject*, or 
a protocol-qualified id
+  // conforming to NSCopying or NSObject.
+
+  block == poorlyQualified1; // expected-error {{invalid operands to binary 
expression ('void (^)()' and 'id')}}
+  block == objectOfWrongType; // expected-error {{invalid operands to binary 
expression ('void (^)()' and 'Test *')}}
+
+  poorlyQualified1 == block; // expected-error {{invalid operands to binary 
expression ('id' and 'void (^)()')}}
+  objectOfWrongType == block; // expected-error {{invalid operands to binary 
expression ('Test *' and 'void (^)()')}}
+
+  return 0;
+}
Index: lib/Sema/SemaExpr.cpp
===
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -7745,8 +7745,8 @@
   return IntToBlockPointer;
 }
 
-// id -> T^
-if (getLangOpts().ObjC1 && RHSType->isObjCIdType()) {
+// id (or strictly compatible object type) -> T^
+if (getLangOpts().ObjC1 && 
RHSType->isBlockCompatibleObjCPointerType(Context)) {
   Kind = CK_AnyPointerToBlockPointerCast;
   return Compatible;
 }
@@ -10028,6 +10028,18 @@
 RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
   return ResultTy;
 }
+
+if(!IsRelational &&
+   LHSType->isBlockPointerType() &&
+   RHSType->isBlockCompatibleObjCPointerType(Context)) {
+  LHS = ImpCastExprToType(LHS.get(), RHSType, 
CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+} else if(!IsRelational &&
+  LHSType->isBlockCompatibleObjCPointerType(Context) &&
+  RHSType->isBlockPointerType()) {
+  RHS = ImpCastExprToType(RHS.get(), LHSType, 
CK_BlockPointerToObjCPointerCast);
+  return ResultTy;
+}
   }
   if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) ||
   (LHSType->isIntegerType() && RHSType->isAnyPointerType())) {


Index: test/Sema/objc_block_compare.mm
===
--- /dev/null
+++ test/Sema/objc_block_compare.mm
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -S -o - -triple i686-windows -verify -fblocks \
+// RUN: -Wno-unused-comparison %s
+
+#pragma clang diagnostic ignored "-Wunused-comparison"
+
+#define nil ((id)nullptr)
+
+@protocol NSObject
+@end
+
+@protocol NSCopying
+@end
+
+@protocol OtherProtocol
+@end
+
+__attribute__((objc_root_class))
+@interface NSObject 
+@end
+
+__attribute__((objc_root_class))
+@interface Test
+@end
+
+int main() {
+  void (^block)() = ^{};
+  NSObject *object;
+  id qualifiedId;
+
+  id poorlyQualified1;
+  Test *objectOfWrongType;
+
+  block == nil;
+  block == object;
+  block == qualifiedId;
+
+  nil == block;
+  object == block;
+  qualifiedId == block;
+
+  // these are still not valid: blocks must be compared with id, NSObject*, or a protocol-qualified id
+  // conforming to NSCopying or NSObject.
+
+  block == poorlyQualified1; // expected-error {{invalid operands to binary expression ('void (^)()' and 'id')}}
+  block == objectOfWrongType; // expected-error {{invalid operands to binary expression ('void (^)()' and 'Test *')}}
+
+  poorlyQualified1 == block; // expected-error {{invalid operands to binary expression ('id' and 'void (^)()')}}
+  objectOfWrongType == block; // expected-error {{invalid operands to binary expression ('Test *' and 'void (^)()')}}
+
+  return 0;
+}
Index: lib/Sema/SemaExpr.cpp
===