[APINotes] Add a 'SwiftImportAsNonGeneric' entry for ObjC classes (#75)

If true, Swift will ignore any 'lightweight generics' parameters when
importing the class, substituting any uses of them with their bounds.
At least, it will once this lands and the Swift side gets implemented.

More rdar://problem/28455962
diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h
index 3f7ab86..b81d273 100644
--- a/include/clang/APINotes/Types.h
+++ b/include/clang/APINotes/Types.h
@@ -212,12 +212,17 @@
   /// Whether this class has designated initializers recorded.
   unsigned HasDesignatedInits : 1;
 
+  unsigned SwiftImportAsNonGenericSpecified : 1;
+  unsigned SwiftImportAsNonGeneric : 1;
+
 public:
   ObjCContextInfo()
     : CommonTypeInfo(),
       HasDefaultNullability(0),
       DefaultNullability(0),
-      HasDesignatedInits(0)
+      HasDesignatedInits(0),
+      SwiftImportAsNonGenericSpecified(false),
+      SwiftImportAsNonGeneric(false)
   { }
 
   /// Determine the default nullability for properties and methods of this
@@ -240,6 +245,21 @@
   bool hasDesignatedInits() const { return HasDesignatedInits; }
   void setHasDesignatedInits(bool value) { HasDesignatedInits = value; }
 
+  Optional<bool> getSwiftImportAsNonGeneric() const {
+    if (SwiftImportAsNonGenericSpecified)
+      return SwiftImportAsNonGeneric;
+    return None;
+  }
+  void setSwiftImportAsNonGeneric(Optional<bool> value) {
+    if (value.hasValue()) {
+      SwiftImportAsNonGenericSpecified = true;
+      SwiftImportAsNonGeneric = value.getValue();
+    } else {
+      SwiftImportAsNonGenericSpecified = false;
+      SwiftImportAsNonGeneric = false;
+    }
+  }
+
   /// Strip off any information within the class information structure that is
   /// module-local, such as 'audited' flags.
   void stripModuleLocalInfo() {
@@ -249,9 +269,9 @@
 
   friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) {
     return static_cast<const CommonTypeInfo &>(lhs) == rhs &&
-           lhs.HasDefaultNullability == rhs.HasDefaultNullability &&
-           lhs.DefaultNullability == rhs.DefaultNullability &&
-           lhs.HasDesignatedInits == rhs.HasDesignatedInits;
+           lhs.getDefaultNullability() == rhs.getDefaultNullability() &&
+           lhs.HasDesignatedInits == rhs.HasDesignatedInits &&
+           lhs.getSwiftImportAsNonGeneric() == rhs.getSwiftImportAsNonGeneric();
   }
 
   friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) {
@@ -270,6 +290,12 @@
       }
     }
 
+    if (!lhs.SwiftImportAsNonGenericSpecified &&
+        rhs.SwiftImportAsNonGenericSpecified) {
+      lhs.SwiftImportAsNonGenericSpecified = true;
+      lhs.SwiftImportAsNonGeneric = rhs.SwiftImportAsNonGeneric;
+    }
+
     lhs.HasDesignatedInits |= rhs.HasDesignatedInits;
 
     return lhs;
@@ -383,6 +409,12 @@
     }
     return lhs;
   }
+
+  friend bool operator==(const ObjCPropertyInfo &lhs,
+                         const ObjCPropertyInfo &rhs) {
+    return static_cast<const VariableInfo &>(lhs) == rhs &&
+           lhs.getSwiftImportAsAccessors() == rhs.getSwiftImportAsAccessors();
+  }
 };
 
 /// Describes a function or method parameter.
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
index 8b7e745..593a4ed 100644
--- a/include/clang/Basic/Attr.td
+++ b/include/clang/Basic/Attr.td
@@ -1506,6 +1506,14 @@
   let Documentation = [Undocumented];
 }
 
+def SwiftImportAsNonGeneric : InheritableAttr {
+  // This attribute has no spellings as it is only ever created implicitly
+  // from API notes.
+  let Spellings = [];
+  let SemaHandler = 0;
+  let Documentation = [Undocumented];
+}
+
 def SwiftImportPropertyAsAccessors : InheritableAttr { 
   // This attribute has no spellings as it is only ever created implicitly
   // from API notes.
diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h
index ef3ef73..1e257f3 100644
--- a/lib/APINotes/APINotesFormat.h
+++ b/lib/APINotes/APINotesFormat.h
@@ -36,7 +36,7 @@
 /// API notes file minor version number.
 ///
 /// When the format changes IN ANY WAY, this number should be incremented.
-const uint16_t VERSION_MINOR = 21;  // Override types
+const uint16_t VERSION_MINOR = 22;  // SwiftImportAsNonGeneric
 
 using IdentifierID = PointerEmbeddedInt<unsigned, 31>;
 using IdentifierIDField = BCVBR<16>;
diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp
index 295e501..587903f 100644
--- a/lib/APINotes/APINotesReader.cpp
+++ b/lib/APINotes/APINotesReader.cpp
@@ -253,6 +253,10 @@
 
       if (payload & 0x4)
         info.setDefaultNullability(static_cast<NullabilityKind>(payload&0x03));
+      payload >>= 3;
+
+      if (payload & (1 << 1))
+        info.setSwiftImportAsNonGeneric(payload & 1);
 
       return info;
     }
diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp
index 116d23d..65edaf9 100644
--- a/lib/APINotes/APINotesWriter.cpp
+++ b/lib/APINotes/APINotesWriter.cpp
@@ -584,8 +584,12 @@
       emitCommonTypeInfo(out, info);
 
       uint8_t payload = 0;
+      if (auto swiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric()) {
+        payload |= (0x01 << 1) | swiftImportAsNonGeneric.getValue();
+      }
+      payload <<= 3;
       if (auto nullable = info.getDefaultNullability()) {
-        payload = (0x01 << 2) | static_cast<uint8_t>(*nullable);
+        payload |= (0x01 << 2) | static_cast<uint8_t>(*nullable);
       }
       payload = (payload << 1) | (info.hasDesignatedInits() ? 1 : 0);
       out << payload;
diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp
index 3621b72..c967646 100644
--- a/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -216,6 +216,7 @@
     StringRef SwiftName;
     Optional<StringRef> SwiftBridge;
     Optional<StringRef> NSErrorDomain;
+    Optional<bool> SwiftImportAsNonGeneric;
     MethodsSeq Methods;
     PropertiesSeq Properties;
   };
@@ -445,6 +446,7 @@
         io.mapOptional("SwiftName",             c.SwiftName);
         io.mapOptional("SwiftBridge",           c.SwiftBridge);
         io.mapOptional("NSErrorDomain",         c.NSErrorDomain);
+        io.mapOptional("SwiftImportAsNonGeneric", c.SwiftImportAsNonGeneric);
         io.mapOptional("Methods",               c.Methods);
         io.mapOptional("Properties",            c.Properties);
       }
@@ -752,6 +754,8 @@
 
       if (cl.AuditedForNullability)
         cInfo.setDefaultNullability(*DefaultNullability);
+      if (cl.SwiftImportAsNonGeneric)
+        cInfo.setSwiftImportAsNonGeneric(*cl.SwiftImportAsNonGeneric);
 
       ContextID clID = Writer->addObjCContext(cl.Name, isClass, cInfo,
                                               swiftVersion);
@@ -1101,6 +1105,7 @@
       record.Name = name;
 
       handleCommonType(record, info);
+      record.SwiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric();
 
       if (info.getDefaultNullability()) {
         record.AuditedForNullability = true;
diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp
index f11f803..f586911 100644
--- a/lib/Sema/SemaAPINotes.cpp
+++ b/lib/Sema/SemaAPINotes.cpp
@@ -603,6 +603,13 @@
 static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D,
                             const api_notes::ObjCContextInfo &info,
                             VersionedInfoMetadata metadata) {
+  if (auto asNonGeneric = info.getSwiftImportAsNonGeneric()) {
+    handleAPINotedAttribute<SwiftImportAsNonGenericAttr>(S, D, *asNonGeneric,
+                                                         metadata, [&] {
+      return SwiftImportAsNonGenericAttr::CreateImplicit(S.Context);
+    });
+  }
+
   // Handle information common to Objective-C classes and protocols.
   ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info,
                   metadata);
diff --git a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
index c3b70b6..bd09953 100644
--- a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
+++ b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
@@ -19,6 +19,8 @@
     Classes:
       - Name: MyReferenceType
         SwiftBridge: ''
+      - Name: TestGenericDUMP
+        SwiftImportAsNonGeneric: true
       - Name: TestProperties
         Properties:
           - Name: accessorsOnlyInVersion3
@@ -34,7 +36,7 @@
             PropertyKind:    Class
             SwiftImportAsAccessors: false
     Functions:
-      - Name: moveToPoint
+      - Name: moveToPointDUMP
         SwiftName: 'moveTo(a:b:)'
       - Name: acceptClosure
         Parameters:      
diff --git a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
index ffbc7df..8301bd1 100644
--- a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
+++ b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
@@ -1,4 +1,4 @@
-void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
+void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
 
 void acceptClosure(void (^ __attribute__((noescape)) block)(void));
 
@@ -28,3 +28,10 @@
 @property (nonatomic, readwrite, retain) id accessorsOnlyExceptInVersion3;
 @property (nonatomic, readwrite, retain, class) id accessorsOnlyForClassExceptInVersion3;
 @end
+
+@interface Base
+@end
+
+@interface TestGenericDUMP<Element> : Base
+- (Element)element;
+@end
diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes
index 2937fa6..4895060 100644
--- a/test/APINotes/Inputs/roundtrip.apinotes
+++ b/test/APINotes/Inputs/roundtrip.apinotes
@@ -9,6 +9,7 @@
     AvailabilityMsg: ''
     SwiftPrivate:    false
     SwiftName:       ''
+    SwiftImportAsNonGeneric: true
     Methods:         
       - Selector:        'cellWithImage:'
         MethodKind:      Class
diff --git a/test/APINotes/versioned.m b/test/APINotes/versioned.m
index 38690fe..29800b6 100644
--- a/test/APINotes/versioned.m
+++ b/test/APINotes/versioned.m
@@ -3,25 +3,31 @@
 // Build and check the unversioned module file.
 // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
 // RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s
-// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s
 
 // Build and check the versioned module file.
 // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
 // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s
-// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s
 
 #import <VersionedKit/VersionedKit.h>
 
-// CHECK-UNVERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
-// CHECK-VERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(a:b:)")));
+// CHECK-UNVERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
+// CHECK-VERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(a:b:)")));
 
-// CHECK-DUMP-LABEL: Dumping moveToPoint
+// CHECK-DUMP-LABEL: Dumping moveToPointDUMP
 // CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 0
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)"
 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)"
 // CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)"
 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0
 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)"
+
+// CHECK-DUMP-LABEL: Dumping TestGenericDUMP
+// CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} Implicit
+// CHECK-UNVERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} Implicit
+
 // CHECK-DUMP-NOT: Dumping
 
 // CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape)));