Merge pull request #6560 from practicalswift/swiftc-28601-getkind-exprkind-binary-isa-tupleexpr-e-binaryexprs-must-have-a-tupleexpr-as-the

[swiftc (25 vs. 5389)] Add crasher in swift::ASTVisitor
diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp
index 6e6d924..693147b 100644
--- a/lib/PrintAsObjC/PrintAsObjC.cpp
+++ b/lib/PrintAsObjC/PrintAsObjC.cpp
@@ -31,6 +31,7 @@
 #include "clang/AST/DeclObjC.h"
 #include "clang/Basic/CharInfo.h"
 #include "clang/Basic/Module.h"
+#include "clang/Lex/Lexer.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/STLExtras.h"
@@ -125,6 +126,38 @@
   return !(firstPiece.size() > 4 && clang::isLowercase(firstPiece[4]));
 }
 
+/// Returns the name of an <os/object.h> type minus the leading "OS_",
+/// or an empty string if \p decl is not an <os/object.h> type.
+static StringRef maybeGetOSObjectBaseName(const clang::NamedDecl *decl) {
+  StringRef name = decl->getName();
+  if (!name.consume_front("OS_"))
+    return StringRef();
+
+  clang::SourceLocation loc = decl->getLocation();
+  if (!loc.isMacroID())
+    return StringRef();
+
+  // Hack: check to see if the name came from a macro in <os/object.h>.
+  clang::SourceManager &sourceMgr = decl->getASTContext().getSourceManager();
+  clang::SourceLocation expansionLoc =
+      sourceMgr.getImmediateExpansionRange(loc).first;
+  clang::SourceLocation spellingLoc = sourceMgr.getSpellingLoc(expansionLoc);
+
+  if (!sourceMgr.getFilename(spellingLoc).endswith("/os/object.h"))
+    return StringRef();
+
+  return name;
+}
+
+/// Returns true if \p decl represents an <os/object.h> type.
+static bool isOSObjectType(const clang::Decl *decl) {
+  auto *named = dyn_cast_or_null<clang::NamedDecl>(decl);
+  if (!named)
+    return false;
+  return !maybeGetOSObjectBaseName(named).empty();
+}
+
+
 namespace {
 using DelayedMemberSet = llvm::SmallSetVector<const ValueDecl *, 32>;
 
@@ -1325,18 +1358,21 @@
     assert(CD->isObjC());
     auto clangDecl = dyn_cast_or_null<clang::NamedDecl>(CD->getClangDecl());
     if (clangDecl) {
-      if (isa<clang::ObjCInterfaceDecl>(clangDecl)) {
+      // Hack for <os/object.h> types, which use classes in Swift but
+      // protocols in Objective-C, and a typedef to hide the difference.
+      StringRef osObjectName = maybeGetOSObjectBaseName(clangDecl);
+      if (!osObjectName.empty()) {
+        os << osObjectName << "_t";
+      } else if (isa<clang::ObjCInterfaceDecl>(clangDecl)) {
         os << clangDecl->getName() << " *";
-        printNullability(optionalKind);
       } else {
         maybePrintTagKeyword(CD);
         os << clangDecl->getName();
-        printNullability(optionalKind);
       }
     } else {
       os << getNameForObjC(CD) << " *";
-      printNullability(optionalKind);
     }
+    printNullability(optionalKind);
   }
 
   void visitProtocolType(ProtocolType *PT, 
@@ -1804,7 +1840,8 @@
 
   bool forwardDeclare(const ClassDecl *CD) {
     if (!CD->isObjC() ||
-        CD->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
+        CD->getForeignClassKind() == ClassDecl::ForeignKind::CFType ||
+        isOSObjectType(CD->getClangDecl())) {
       return false;
     }
     forwardDeclare(CD, [&]{ os << "@class " << getNameForObjC(CD) << ";\n"; });
diff --git a/test/PrintAsObjC/dispatch.swift b/test/PrintAsObjC/dispatch.swift
new file mode 100644
index 0000000..40282b8
--- /dev/null
+++ b/test/PrintAsObjC/dispatch.swift
@@ -0,0 +1,16 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %target-swift-frontend -typecheck %s -parse-as-library -emit-objc-header-path %t/swift.h
+// RUN: %FileCheck %s < %t/swift.h
+
+// REQUIRES: objc_interop
+
+import Foundation
+
+// CHECK: @import Dispatch;
+
+// CHECK-LABEL: @interface Test : NSObject{{$}}
+public class Test : NSObject { 
+  // CHECK-NEXT: - (void)thank:(dispatch_queue_t _Nonnull)queue;
+  public func thank(_ queue: DispatchQueue) {}
+  // CHECK-NEXT: init
+} // CHECK-NEXT: @end