Merge pull request #21112 from slavapestov/on-demand-accessor-fix

Force SILGen emission of on-demand synthesized accessors
diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp
index 3960392..73cf2bd 100644
--- a/lib/SILGen/SILGenDecl.cpp
+++ b/lib/SILGen/SILGenDecl.cpp
@@ -1396,7 +1396,19 @@
   lastEmittedConformance = root;
 }
 
+static bool isDeclaredInPrimaryFile(SILModule &M, Decl *d) {
+  auto *dc = d->getDeclContext();
+  if (auto *sf = dyn_cast<SourceFile>(dc->getModuleScopeContext()))
+    if (M.isWholeModule() || M.getAssociatedContext() == sf)
+      return true;
+
+  return false;
+}
+
 void SILGenModule::emitExternalDefinition(Decl *d) {
+  if (isDeclaredInPrimaryFile(M, d))
+    return;
+
   switch (d->getKind()) {
   case DeclKind::Func:
   case DeclKind::Accessor: {
diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp
index 912c8f3..caab899 100644
--- a/lib/Sema/CodeSynthesis.cpp
+++ b/lib/Sema/CodeSynthesis.cpp
@@ -1078,6 +1078,11 @@
     if (isOnDemandAccessor(storage, kind)) {
       auto synthKind = getSynthKindForAccessorKind(kind);
       triggerSynthesis(*this, storage->getAccessor(kind), synthKind);
+
+      // Make sure SILGen emits the accessor; on-demand accessors have shared
+      // linkage, and if its defined in a different translation unit from the
+      // conformance we cannot simply generate an external declaration.
+      Context.addExternalDecl(storage->getAccessor(kind));
     }
   });
 
diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp
index 68a2cbf..92ee12a 100644
--- a/lib/Sema/TypeChecker.cpp
+++ b/lib/Sema/TypeChecker.cpp
@@ -453,6 +453,17 @@
       TC.typeCheckAbstractFunctionBody(AFD);
     }
 
+    // Synthesize any necessary function bodies.
+    // FIXME: If we're not planning to run SILGen, this is wasted effort.
+    while (!TC.FunctionsToSynthesize.empty()) {
+      auto function = TC.FunctionsToSynthesize.back().second;
+      TC.FunctionsToSynthesize.pop_back();
+      if (function.getDecl()->isInvalid() || TC.Context.hadError())
+        continue;
+
+      TC.synthesizeFunctionBody(function);
+    }
+
     // Type check external definitions.
     for (unsigned n = TC.Context.ExternalDefinitions.size();
          currentExternalDef != n;
@@ -483,17 +494,6 @@
       TC.validateDecl(decl);
     }
 
-    // Synthesize any necessary function bodies.
-    // FIXME: If we're not planning to run SILGen, this is wasted effort.
-    while (!TC.FunctionsToSynthesize.empty()) {
-      auto function = TC.FunctionsToSynthesize.back().second;
-      TC.FunctionsToSynthesize.pop_back();
-      if (function.getDecl()->isInvalid() || TC.Context.hadError())
-        continue;
-
-      TC.synthesizeFunctionBody(function);
-    }
-
     // Validate any referenced declarations for SIL's purposes.
     // Note: if we ever start putting extension members in vtables, we'll need
     // to validate those members too.
diff --git a/test/SILGen/Inputs/nsmanaged-witness-multi-other.swift b/test/SILGen/Inputs/nsmanaged-witness-multi-other.swift
new file mode 100644
index 0000000..cfdf815
--- /dev/null
+++ b/test/SILGen/Inputs/nsmanaged-witness-multi-other.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+public class Fish {
+  @NSManaged public var name: String
+}
diff --git a/test/SILGen/nsmanaged-witness-multi.swift b/test/SILGen/nsmanaged-witness-multi.swift
new file mode 100644
index 0000000..a8bb1e0
--- /dev/null
+++ b/test/SILGen/nsmanaged-witness-multi.swift
@@ -0,0 +1,28 @@
+// RUN: %empty-directory(%t)
+
+// RUN: %target-swift-frontend -module-name main -emit-silgen -enable-sil-ownership -sdk %S/Inputs -primary-file %s %S/Inputs/nsmanaged-witness-multi-other.swift -I %S/Inputs -I %t -enable-source-import | %FileCheck %s
+
+// RUN: %target-swift-frontend -module-name main -emit-silgen -enable-sil-ownership -sdk %S/Inputs -primary-file %s -primary-file %S/Inputs/nsmanaged-witness-multi-other.swift -I %S/Inputs -I %t -enable-source-import | %FileCheck %s
+
+// RUN: %target-swift-frontend -module-name main -emit-silgen -enable-sil-ownership -sdk %S/Inputs %s %S/Inputs/nsmanaged-witness-multi-other.swift -I %S/Inputs -I %t -enable-source-import | %FileCheck %s
+
+// REQUIRES: objc_interop
+import Foundation
+
+public protocol FishProtocol {
+  var name: String { get set }
+}
+
+extension Fish : FishProtocol {}
+
+// Make sure the modify accessor for Fish.name is emitted here even though it
+// its storage was declared in a different translation unit
+
+// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] @$s4main4FishCAA0B8ProtocolA2aDP4nameSSvMTW : $@yield_once @convention(witness_method: FishProtocol) (@inout Fish) -> @yields @inout String
+// CHECK: function_ref @$s4main4FishC4nameSSvM
+// CHECK: return
+
+// CHECK-LABEL: sil shared [serialized] @$s4main4FishC4nameSSvM : $@yield_once @convention(method) (@guaranteed Fish) -> @yields @inout String
+// CHECK: objc_method %0 : $Fish, #Fish.name!getter.1.foreign
+// CHECK: objc_method %0 : $Fish, #Fish.name!setter.1.foreign
+// CHECK: unwind