mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
AST: Static properties of fixed-layout types are resilient
We don't want @_fixed_layout to apply to static properties, or you get fun consequences like not being able to change static stored properties defined in extensions of imported types to computed.
This commit is contained in:
@@ -4310,12 +4310,19 @@ public:
|
|||||||
AccessStrategy getAccessStrategy(AccessSemantics semantics,
|
AccessStrategy getAccessStrategy(AccessSemantics semantics,
|
||||||
AccessKind accessKind) const;
|
AccessKind accessKind) const;
|
||||||
|
|
||||||
/// \brief Do we need to use resilient access patterns outside of this type's
|
/// \brief Should this declaration behave as if it must be accessed
|
||||||
/// resilience domain?
|
/// 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
|
||||||
|
/// property's resilience domain?
|
||||||
bool isResilient() const;
|
bool isResilient() const;
|
||||||
|
|
||||||
/// \brief Do we need to use resilient access patterns when accessing this
|
/// \brief Do we need to use resilient access patterns when accessing this
|
||||||
/// type from the given module?
|
/// property from the given module?
|
||||||
bool isResilient(ModuleDecl *M, ResilienceExpansion expansion) const;
|
bool isResilient(ModuleDecl *M, ResilienceExpansion expansion) const;
|
||||||
|
|
||||||
/// Does the storage use a behavior?
|
/// Does the storage use a behavior?
|
||||||
|
|||||||
@@ -1472,16 +1472,10 @@ bool ValueDecl::isOutermostPrivateOrFilePrivateScope() const {
|
|||||||
!isInPrivateOrLocalContext(this);
|
!isInPrivateOrLocalContext(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AbstractStorageDecl::isResilient() const {
|
bool AbstractStorageDecl::isFormallyResilient() const {
|
||||||
// If we're in a nominal type, just query the type.
|
// Check for an explicit @_fixed_layout attribute.
|
||||||
auto *dc = getDeclContext();
|
if (getAttrs().hasAttribute<FixedLayoutAttr>())
|
||||||
|
return false;
|
||||||
if (dc->isTypeContext()) {
|
|
||||||
auto *nominalDecl = dc->getAsNominalTypeOrNominalTypeExtensionContext();
|
|
||||||
if (nominalDecl == nullptr)
|
|
||||||
return false;
|
|
||||||
return nominalDecl->isResilient();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private and (unversioned) internal variables always have a
|
// Private and (unversioned) internal variables always have a
|
||||||
// fixed layout.
|
// fixed layout.
|
||||||
@@ -1489,12 +1483,19 @@ bool AbstractStorageDecl::isResilient() const {
|
|||||||
/*respectVersionedAttr=*/true).isPublic())
|
/*respectVersionedAttr=*/true).isPublic())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check for an explicit @_fixed_layout attribute.
|
// If we're an instance property of a nominal type, query the type.
|
||||||
if (getAttrs().hasAttribute<FixedLayoutAttr>())
|
auto *dc = getDeclContext();
|
||||||
|
if (!isStatic())
|
||||||
|
if (auto *nominalDecl = dc->getAsNominalTypeOrNominalTypeExtensionContext())
|
||||||
|
return nominalDecl->isResilient();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AbstractStorageDecl::isResilient() const {
|
||||||
|
if (!isFormallyResilient())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Must use resilient access patterns.
|
|
||||||
assert(getDeclContext()->isModuleScopeContext());
|
|
||||||
switch (getDeclContext()->getParentModule()->getResilienceStrategy()) {
|
switch (getDeclContext()->getParentModule()->getResilienceStrategy()) {
|
||||||
case ResilienceStrategy::Resilient:
|
case ResilienceStrategy::Resilient:
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -276,9 +276,8 @@ static void maybeMarkTransparent(FuncDecl *accessor,
|
|||||||
if (!nominalDecl)
|
if (!nominalDecl)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Accessors for stored properties of resilient types are not
|
// Accessors for resilient properties are not @_transparent.
|
||||||
// @_transparent.
|
if (storage->isResilient())
|
||||||
if (nominalDecl->isResilient())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Accessors for protocol storage requirements are never @_transparent
|
// Accessors for protocol storage requirements are never @_transparent
|
||||||
|
|||||||
@@ -48,3 +48,24 @@ private let privateGlobal = 0
|
|||||||
struct AnotherInternalStruct {
|
struct AnotherInternalStruct {
|
||||||
var storedProperty = privateGlobal
|
var storedProperty = privateGlobal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Static properties in fixed-layout type is still resilient
|
||||||
|
|
||||||
|
@_fixed_layout
|
||||||
|
public struct HasStaticProperty {
|
||||||
|
public static var staticProperty: Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil @$S22fixed_layout_attribute18usesStaticPropertyyyF : $@convention(thin) () -> ()
|
||||||
|
// CHECK: function_ref @$S22fixed_layout_attribute17HasStaticPropertyV06staticF0Sivau : $@convention(thin) () -> Builtin.RawPointer
|
||||||
|
// CHECK: return
|
||||||
|
public func usesStaticProperty() {
|
||||||
|
_ = HasStaticProperty.staticProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [serialized] @$S22fixed_layout_attribute28usesStaticPropertyInlineableyyF : $@convention(thin) () -> ()
|
||||||
|
|
||||||
|
@_inlineable
|
||||||
|
public func usesStaticPropertyInlineable() {
|
||||||
|
_ = HasStaticProperty.staticProperty
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public struct ChangeStoredToComputed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var fahrenheit: Int = 32
|
public var fahrenheit: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
#if BEFORE
|
||||||
|
|
||||||
|
@_fixed_layout
|
||||||
|
public struct ChangeStoredToComputed {
|
||||||
|
public static var celsius: Int = 0
|
||||||
|
|
||||||
|
public static var fahrenheit: Int {
|
||||||
|
get {
|
||||||
|
return (celsius * 9) / 5 + 32
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
celsius = ((newValue - 32) * 5) / 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
@_fixed_layout
|
||||||
|
public struct ChangeStoredToComputed {
|
||||||
|
public static var celsius: Int {
|
||||||
|
get {
|
||||||
|
return ((fahrenheit - 32) * 5) / 9
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
fahrenheit = (newValue * 9) / 5 + 32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var fahrenheit: Int = 32
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// RUN: %target-resilience-test
|
||||||
|
// REQUIRES: executable_test
|
||||||
|
|
||||||
|
import StdlibUnittest
|
||||||
|
import struct_change_stored_to_computed_static
|
||||||
|
|
||||||
|
|
||||||
|
var ChangeStoredToComputedTest = TestSuite("ChangeStoredToComputed")
|
||||||
|
|
||||||
|
ChangeStoredToComputedTest.test("ChangeStoredToComputed") {
|
||||||
|
do {
|
||||||
|
expectEqual(ChangeStoredToComputed.celsius, 0)
|
||||||
|
expectEqual(ChangeStoredToComputed.fahrenheit, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
ChangeStoredToComputed.celsius = 10
|
||||||
|
expectEqual(ChangeStoredToComputed.celsius, 10)
|
||||||
|
expectEqual(ChangeStoredToComputed.fahrenheit, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
func increaseTemperature(_ t: inout Int) {
|
||||||
|
t += 10
|
||||||
|
}
|
||||||
|
|
||||||
|
increaseTemperature(&ChangeStoredToComputed.celsius)
|
||||||
|
|
||||||
|
expectEqual(ChangeStoredToComputed.celsius, 20)
|
||||||
|
expectEqual(ChangeStoredToComputed.fahrenheit, 68)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runAllTests()
|
||||||
Reference in New Issue
Block a user