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()
+}
+