Merge pull request #13897 from slavapestov/fixed-layout-property-initializer-restriction

Fixed layout property initializer restriction
diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h
index 088b7ec..346601d 100644
--- a/include/swift/AST/Decl.h
+++ b/include/swift/AST/Decl.h
@@ -2787,6 +2787,13 @@
   
   void setBraces(SourceRange braces) { Braces = braces; }
 
+  /// \brief Should this declaration behave as if it must be accessed
+  /// resiliently, even when we're building a non-resilient module?
+  ///
+  /// This is used for diagnostics, because we do not want a behavior
+  /// change between builds with resilience enabled and disabled.
+  bool isFormallyResilient() const;
+
   /// \brief Do we need to use resilient access patterns outside of this type's
   /// resilience domain?
   bool isResilient() const;
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index 1bb08a1..ad19889 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -3703,7 +3703,8 @@
   "%select{a '@_transparent' function|" \
   "an '@inline(__always)' function|" \
   "an '@_inlineable' function|" \
-  "a default argument value}"
+  "a default argument value|" \
+  "a property initializer in a '@_fixed_layout' type}"
 
 ERROR(local_type_in_inlineable_function,
       none, "type %0 cannot be nested inside " FRAGILE_FUNC_KIND "1",
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 12b2f4e..ae8b248 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -2273,7 +2273,7 @@
   return 0;
 }
 
-bool NominalTypeDecl::isResilient() const {
+bool NominalTypeDecl::isFormallyResilient() const {
   // Private and (unversioned) internal types always have a
   // fixed layout.
   if (!getFormalAccessScope(/*useDC=*/nullptr,
@@ -2293,7 +2293,18 @@
   if ((isa<EnumDecl>(this) || isa<ProtocolDecl>(this)) && isObjC())
     return false;
 
-  // Otherwise, access via indirect "resilient" interfaces.
+  // Otherwise, the declaration behaves as if it was accessed via indirect
+  // "resilient" interfaces, even if the module is not built with resilience.
+  return true;
+}
+
+bool NominalTypeDecl::isResilient() const {
+  // If we're not formally resilient, don't check the module resilience
+  // strategy.
+  if (!isFormallyResilient())
+    return false;
+
+  // Otherwise, check the module.
   switch (getParentModule()->getResilienceStrategy()) {
   case ResilienceStrategy::Resilient:
     return true;
diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp
index 4eb4c1c..3f72f08 100644
--- a/lib/AST/DeclContext.cpp
+++ b/lib/AST/DeclContext.cpp
@@ -494,6 +494,21 @@
           ->getDefaultArgumentResilienceExpansion();
     }
 
+    // Stored property initializer contexts use minimal resilience expansion
+    // if the type is formally fixed layout.
+    if (isa<PatternBindingInitializer>(dc)) {
+      if (auto *NTD = dyn_cast<NominalTypeDecl>(dc->getParent())) {
+        if (!NTD->getFormalAccessScope(/*useDC=*/nullptr,
+                                       /*respectVersionedAttr=*/true).isPublic())
+          return ResilienceExpansion::Maximal;
+
+        if (NTD->isFormallyResilient())
+          return ResilienceExpansion::Maximal;
+
+        return ResilienceExpansion::Minimal;
+      }
+    }
+
     if (auto *AFD = dyn_cast<AbstractFunctionDecl>(dc)) {
       // If the function is a nested function, we will serialize its body if
       // we serialize the parent's body.
diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp
index 633507b..2d4a8a6 100644
--- a/lib/Sema/ResilienceDiagnostics.cpp
+++ b/lib/Sema/ResilienceDiagnostics.cpp
@@ -26,8 +26,10 @@
 FragileFunctionKind TypeChecker::getFragileFunctionKind(const DeclContext *DC) {
   for (; DC->isLocalContext(); DC = DC->getParent()) {
     if (auto *DAI = dyn_cast<DefaultArgumentInitializer>(DC))
-      if (DAI->getResilienceExpansion() == ResilienceExpansion::Minimal)
-        return FragileFunctionKind::DefaultArgument;
+      return FragileFunctionKind::DefaultArgument;
+
+    if (auto *PBI = dyn_cast<PatternBindingInitializer>(DC))
+      return FragileFunctionKind::PropertyInitializer;
 
     if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DC)) {
       // If the function is a nested function, we will serialize its body if
diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h
index 7ff6a11..df7b7b8 100644
--- a/lib/Sema/TypeChecker.h
+++ b/lib/Sema/TypeChecker.h
@@ -2297,7 +2297,8 @@
     Transparent,
     InlineAlways,
     Inlineable,
-    DefaultArgument
+    DefaultArgument,
+    PropertyInitializer
   };
 
   /// Given that \p DC is within a fragile context for some reason, describe
diff --git a/stdlib/public/core/RuntimeFunctionCounters.swift b/stdlib/public/core/RuntimeFunctionCounters.swift
index d5d8e24..5587ad2 100644
--- a/stdlib/public/core/RuntimeFunctionCounters.swift
+++ b/stdlib/public/core/RuntimeFunctionCounters.swift
@@ -89,7 +89,6 @@
 
 // This is a namespace for runtime functions related to management
 // of runtime function counters.
-@_fixed_layout // FIXME(sil-serialize-all)
 public // @testable
 struct _RuntimeFunctionCounters {
 #if os(Windows) && arch(x86_64)
@@ -228,8 +227,6 @@
 // counters. This type should not be used directly. You should use its
 // wrappers GlobalRuntimeFunctionCountersState and
 // ObjectRuntimeFunctionCountersState instead.
-@_fixed_layout // FIXME(sil-serialize-all)
-@_versioned // FIXME(sil-serialize-all)
 internal struct _RuntimeFunctionCountersState: _RuntimeFunctionCountersStats {
   /// Reserve enough space for 64 elements.
   typealias Counters =
@@ -420,7 +417,6 @@
 
 /// This type should be used to collect statistics about the global runtime
 /// function pointers.
-@_fixed_layout // FIXME(sil-serialize-all)
 public // @testable
 struct _GlobalRuntimeFunctionCountersState: _RuntimeFunctionCountersStats {
   var state = _RuntimeFunctionCountersState()
@@ -458,7 +454,6 @@
 
 /// This type should be used to collect statistics about object runtime
 /// function pointers.
-@_fixed_layout // FIXME(sil-serialize-all)
 public // @testable
 struct _ObjectRuntimeFunctionCountersState: _RuntimeFunctionCountersStats {
   var state = _RuntimeFunctionCountersState()
diff --git a/test/attr/attr_inlineable.swift b/test/attr/attr_inlineable.swift
index a564f5f..6a8ea31 100644
--- a/test/attr/attr_inlineable.swift
+++ b/test/attr/attr_inlineable.swift
@@ -1,6 +1,7 @@
 // RUN: %target-typecheck-verify-swift -swift-version 4
 // RUN: %target-typecheck-verify-swift -swift-version 4 -enable-testing
-
+// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-resilience
+// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-resilience -enable-testing
 @_inlineable struct TestInlineableStruct {}
 // expected-error@-1 {{'@_inlineable' attribute cannot be applied to this declaration}}
 
@@ -185,15 +186,18 @@
 
 // Inherited initializers - <rdar://problem/34398148>
 @_versioned
+@_fixed_layout
 class Base {
   @_versioned
   init(x: Int) {}
 }
 
 @_versioned
+@_fixed_layout
 class Middle : Base {}
 
 @_versioned
+@_fixed_layout
 class Derived : Middle {
   @_versioned
   @_inlineable
@@ -201,3 +205,29 @@
     super.init(x: y)
   }
 }
+
+// Stored property initializer expressions.
+//
+// Note the behavior here does not depend on the state of the -enable-resilience
+// flag; the test runs with both the flag on and off. Only the explicit
+// presence of a '@_fixed_layout' attribute determines the behavior here.
+
+let internalGlobal = 0
+// expected-note@-1 {{let 'internalGlobal' is not '@_versioned' or public}}
+public let publicGlobal = 0
+
+struct InternalStructWithInit {
+  var x = internalGlobal // OK
+  var y = publicGlobal // OK
+}
+
+public struct PublicResilientStructWithInit {
+  var x = internalGlobal // OK
+  var y = publicGlobal // OK
+}
+
+@_fixed_layout
+public struct PublicFixedStructWithInit {
+  var x = internalGlobal // expected-error {{let 'internalGlobal' is internal and cannot be referenced from a property initializer in a '@_fixed_layout' type}}
+  var y = publicGlobal // OK
+}
diff --git a/validation-test/compiler_crashers_2_fixed/0137-sr6730.swift b/validation-test/compiler_crashers_2_fixed/0137-sr6730.swift
new file mode 100644
index 0000000..84a76f4
--- /dev/null
+++ b/validation-test/compiler_crashers_2_fixed/0137-sr6730.swift
@@ -0,0 +1,42 @@
+// RUN: not %target-swift-frontend %s -typecheck
+
+public protocol OptionalProtocol {
+  associatedtype Wrapped
+
+  var optional: Wrapped? { get }
+}
+
+extension Optional: OptionalProtocol {
+  public var optional: Wrapped? {
+    return self
+  }
+}
+
+public extension Sequence where Element: OptionalProtocol {
+  func skipNil() -> [Element.Wrapped] {
+    return self
+    .compactMap { $0.optional }
+  }
+}
+
+class A {}
+class A1: A {}
+class A2: A {}
+class A3: A {}
+
+final class V {
+  init() {
+    ([
+      self.a1, self.a2, self.a3
+      ] as [A])
+    .skipNil()
+    .forEach { self.f($0) }
+  }
+
+  func f(_ a: A) {}
+
+  private let a1 = A1()
+  private let a2 = A2()
+  private let a3 = A3()
+}
+