Skip to content

Commit 220934d

Browse files
committed
[move-only] Ban resilient noncopyable types.
One can still in resilient frameworks have noncopyable frozen types. This means that one cannot make a noncopyable: 1. Full resilient public type. 2. @usableFromInline type. NOTE: One can still use a frozen noncopyable type as a usableFromInline class field. I validated in the attached tests that we get the correct code generation. I also eliminated a small bug in TypeCheckDeclPrimary where we weren't using a requestified attr check and instead were checking directly. rdar://111125845 (cherry picked from commit d0938a9)
1 parent 9989e6c commit 220934d

12 files changed

+134
-13
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7224,6 +7224,11 @@ ERROR(noimplicitcopy_attr_invalid_in_generic_context,
72247224
none, "'@_noImplicitCopy' attribute cannot be applied to entities in generic contexts", ())
72257225
ERROR(noimplicitcopy_attr_not_allowed_on_moveonlytype,none,
72267226
"'@_noImplicitCopy' has no effect when applied to a noncopyable type", ())
7227+
ERROR(noncopyable_types_cannot_be_resilient, none,
7228+
"noncopyable %0 %1 must be @frozen in library evolution mode; "
7229+
"non-@frozen public and @usableFromInline noncopyable types are not "
7230+
"supported",
7231+
(DescriptiveDeclKind, Identifier))
72277232

72287233
//------------------------------------------------------------------------------
72297234
// MARK: Runtime discoverable attributes (@runtimeMetadata)

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ EXPERIMENTAL_FEATURE(NoImplicitCopy, true)
129129
EXPERIMENTAL_FEATURE(OldOwnershipOperatorSpellings, true)
130130
EXPERIMENTAL_FEATURE(MoveOnlyEnumDeinits, true)
131131
EXPERIMENTAL_FEATURE(MoveOnlyTuples, true)
132+
EXPERIMENTAL_FEATURE(MoveOnlyResilientTypes, true)
132133

133134
EXPERIMENTAL_FEATURE(OneWayClosureParameters, false)
134135
EXPERIMENTAL_FEATURE(TypeWitnessSystemInference, false)

lib/AST/ASTPrinter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3294,6 +3294,12 @@ static bool usesFeatureMoveOnlyEnumDeinits(Decl *decl) {
32943294
return false;
32953295
}
32963296

3297+
static bool usesFeatureMoveOnlyResilientTypes(Decl *decl) {
3298+
if (auto *nomDecl = dyn_cast<NominalTypeDecl>(decl))
3299+
return nomDecl->isResilient() && usesFeatureMoveOnly(decl);
3300+
return false;
3301+
}
3302+
32973303
static bool usesFeatureOneWayClosureParameters(Decl *decl) {
32983304
return false;
32993305
}

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2672,7 +2672,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26722672

26732673
// If our enum is marked as move only, it cannot be indirect or have any
26742674
// indirect cases.
2675-
if (ED->getAttrs().hasAttribute<MoveOnlyAttr>()) {
2675+
if (ED->isMoveOnly()) {
26762676
if (ED->isIndirect())
26772677
ED->diagnose(diag::noncopyable_enums_do_not_support_indirect,
26782678
ED->getBaseIdentifier());
@@ -2682,6 +2682,13 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26822682
ED->getBaseIdentifier());
26832683
}
26842684
}
2685+
2686+
if (!ED->getASTContext().LangOpts.hasFeature(
2687+
Feature::MoveOnlyResilientTypes) &&
2688+
ED->isResilient()) {
2689+
ED->diagnose(diag::noncopyable_types_cannot_be_resilient,
2690+
ED->getDescriptiveKind(), ED->getBaseIdentifier());
2691+
}
26852692
}
26862693
}
26872694

@@ -2722,6 +2729,13 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
27222729
diagnoseCopyableTypeContainingMoveOnlyType(SD);
27232730

27242731
diagnoseIncompatibleProtocolsForMoveOnlyType(SD);
2732+
2733+
if (!SD->getASTContext().LangOpts.hasFeature(
2734+
Feature::MoveOnlyResilientTypes) &&
2735+
SD->isResilient() && SD->isMoveOnly()) {
2736+
SD->diagnose(diag::noncopyable_types_cannot_be_resilient,
2737+
SD->getDescriptiveKind(), SD->getBaseIdentifier());
2738+
}
27252739
}
27262740

27272741
/// Check whether the given properties can be @NSManaged in this class.

test/IRGen/moveonly_split_module_source_deinit_library_evolution.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-build-swift-dylib(%t/%target-library-name(MoveOnlySplit)) -enable-library-evolution %S/Inputs/moveonly_split_module_source_input.swift -emit-module -emit-module-path %t/MoveOnlySplit.swiftmodule -module-name MoveOnlySplit -DTEST_LIBRARY_EVOLUTION
2+
// RUN: %target-build-swift-dylib(%t/%target-library-name(MoveOnlySplit)) -enable-library-evolution %S/Inputs/moveonly_split_module_source_input.swift -emit-module -emit-module-path %t/MoveOnlySplit.swiftmodule -module-name MoveOnlySplit -DTEST_LIBRARY_EVOLUTION -enable-experimental-feature MoveOnlyResilientTypes
33
// RUN: %target-codesign %t/%target-library-name(MoveOnlySplit)
44

5-
// RUN: %target-build-swift %s -lMoveOnlySplit -I %t -L %t -o %t/main %target-rpath(%t)
5+
// RUN: %target-build-swift %s -lMoveOnlySplit -I %t -L %t -o %t/main %target-rpath(%t) -enable-experimental-feature MoveOnlyResilientTypes
66
// RUN: %target-codesign %t/main
77
// RUN: %target-run %t/main %t/%target-library-name(MoveOnlySplit) | %FileCheck -check-prefix=CHECK-LIBRARY-EVOLUTION %s
88

test/ModuleInterface/discard_interface.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -module-name Library -verify
3-
// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -I %t
2+
// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -module-name Library -verify -enable-experimental-feature MoveOnlyResilientTypes
3+
// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -I %t -enable-experimental-feature MoveOnlyResilientTypes
44
// RUN: %FileCheck %s < %t/Library.swiftinterface
55

66
// This test makes sure that discard and _forget are emitted correctly in the

test/ModuleInterface/moveonly_interface_flag.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -module-name Library
3-
// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -I %t
1+
7// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -module-name Library -enable-experimental-feature MoveOnlyResilientTypes
3+
// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -I %t -enable-experimental-feature MoveOnlyResilientTypes
44
// RUN: %FileCheck %s < %t/Library.swiftinterface
55

66
// this test makes sure that decls containing a move-only type are guarded by the $MoveOnly feature flag

test/ModuleInterface/moveonly_user.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// RUN: %target-swift-frontend -emit-sil -sil-verify-all -I %t %s > /dev/null
66

77
// >> now again with library evolution; we expect the same result.
8-
// RUN: %target-swift-frontend -DSYNTHESIZE_ACCESSORS -enable-library-evolution -emit-module -o %t/Hello.swiftmodule %S/Inputs/moveonly_api.swift
9-
// RUN: %target-swift-frontend -emit-sil -sil-verify-all -I %t %s > /dev/null
8+
// RUN: %target-swift-frontend -DSYNTHESIZE_ACCESSORS -enable-library-evolution -enable-experimental-feature MoveOnlyResilientTypes -emit-module -o %t/Hello.swiftmodule %S/Inputs/moveonly_api.swift
9+
// RUN: %target-swift-frontend -enable-experimental-feature MoveOnlyResilientTypes -emit-sil -sil-verify-all -I %t %s > /dev/null
1010

1111
// FIXME: ideally this would also try executing the program rather than just generating SIL
1212

test/SILGen/moveonly_library_evolution.swift

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-swift-emit-silgen -enable-experimental-feature NoImplicitCopy -enable-library-evolution %s | %FileCheck %s
1+
// RUN: %target-swift-emit-silgen -enable-experimental-feature NoImplicitCopy -enable-experimental-feature MoveOnlyResilientTypes -enable-library-evolution %s | %FileCheck %s
2+
// RUN: %target-swift-emit-sil -O -sil-verify-all -enable-experimental-feature NoImplicitCopy -enable-experimental-feature MoveOnlyResilientTypes -enable-library-evolution %s
23

34
////////////////////////
45
// MARK: Declarations //
@@ -48,3 +49,60 @@ public struct DeinitTest : ~Copyable {
4849
public func callerArgumentSpillingTestArg(_ x: CopyableKlass) {
4950
borrowVal(x.letStruct.e)
5051
}
52+
53+
/////////////////////////////////////
54+
// MARK: UsableFromInline in Class //
55+
/////////////////////////////////////
56+
57+
public class CopyableKlass2 {
58+
public init() {}
59+
}
60+
61+
@frozen
62+
public struct E : ~Copyable {
63+
var x = CopyableKlass2()
64+
}
65+
66+
public class UsableFromInlineTestKlass {
67+
// Read accessor
68+
//
69+
// CHECK-LABEL: sil [ossa] @$s26moveonly_library_evolution25UsableFromInlineTestKlassC1eAA1EVvr : $@yield_once @convention(method) (@guaranteed UsableFromInlineTestKlass) -> @yields @guaranteed E {
70+
// CHECK: bb0([[ARG:%.*]] : @guaranteed
71+
// CHECK: [[FIELD:%.*]] = ref_element_addr [[ARG]]
72+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[FIELD]]
73+
// CHECK: [[MARK:%.*]] = mark_must_check [no_consume_or_assign] [[ACCESS]]
74+
// CHECK: [[LOAD:%.*]] = load [copy] [[MARK]]
75+
// CHECK: yield [[LOAD]]
76+
// CHECK: } // end sil function '$s26moveonly_library_evolution25UsableFromInlineTestKlassC1eAA1EVvr'
77+
78+
// Setter
79+
// CHECK-LABEL: sil [ossa] @$s26moveonly_library_evolution25UsableFromInlineTestKlassC1eAA1EVvs : $@convention(method) (@owned E, @guaranteed UsableFromInlineTestKlass) -> () {
80+
// CHECK: bb0([[NEW_VALUE:%.*]] : @owned $E, [[SELF:%.*]] : @guaranteed
81+
// CHECK: [[VALUE:%.*]] = alloc_box ${ let E }
82+
// CHECK: [[PROJECT:%.*]] = project_box [[VALUE]]
83+
// CHECK: store [[NEW_VALUE]] to [init] [[PROJECT]]
84+
// CHECK: [[MARK:%.*]] = mark_must_check [no_consume_or_assign] [[PROJECT]]
85+
// CHECK: [[LOAD:%.*]] = load [copy] [[MARK]]
86+
// CHECK: [[REF:%.*]] = ref_element_addr [[SELF]]
87+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [dynamic] [[REF]]
88+
// CHECK: [[MARK:%.*]] = mark_must_check [assignable_but_not_consumable] [[ACCESS]]
89+
// CHECK: assign [[LOAD]] to [[MARK]]
90+
// CHECK: } // end sil function '$s26moveonly_library_evolution25UsableFromInlineTestKlassC1eAA1EVvs'
91+
92+
// Modify
93+
// CHECK-LABEL: sil [ossa] @$s26moveonly_library_evolution25UsableFromInlineTestKlassC1eAA1EVvM : $@yield_once @convention(method) (@guaranteed UsableFromInlineTestKlass) -> @yields @inout E {
94+
// CHECK: bb0([[ARG:%.*]] : @guaranteed
95+
// CHECK: [[FIELD:%.*]] = ref_element_addr [[ARG]]
96+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [dynamic] [[FIELD]]
97+
// CHECK: [[MARK:%.*]] = mark_must_check [assignable_but_not_consumable] [[ACCESS]]
98+
// CHECK: yield [[MARK]]
99+
// CHECK: } // end sil function '$s26moveonly_library_evolution25UsableFromInlineTestKlassC1eAA1EVvM'
100+
@usableFromInline
101+
var e = E()
102+
}
103+
104+
105+
func useUsableFromInlineTestKlass() {
106+
let k = UsableFromInlineTestKlass()
107+
k.e = E()
108+
}

test/SILOptimizer/moveonly_addresschecker_diagnostics_library_evolution.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-emit-sil -enable-experimental-feature NoImplicitCopy -sil-verify-all -verify -enable-library-evolution %s
1+
// RUN: %target-swift-emit-sil -enable-experimental-feature NoImplicitCopy -sil-verify-all -verify -enable-library-evolution -enable-experimental-feature MoveOnlyResilientTypes %s
22

33
// This test is used to validate that we properly handle library evolution code
44
// until we can get all of the normal moveonly_addresschecker_diagnostics test

test/Sema/discard_module.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// RUN: %target-typecheck-verify-swift -I %t
66

77
// >> now again with library evolution; we expect the same result.
8-
// RUN: %target-swift-frontend -enable-library-evolution -emit-module -o %t/SorryModule.swiftmodule %S/Inputs/discard_module_defining.swift %S/Inputs/discard_module_adjacent.swift
8+
// RUN: %target-swift-frontend -enable-library-evolution -emit-module -o %t/SorryModule.swiftmodule %S/Inputs/discard_module_defining.swift %S/Inputs/discard_module_adjacent.swift -enable-experimental-feature MoveOnlyResilientTypes
99
// RUN: %target-typecheck-verify-swift -I %t
1010

1111
// "Sorry" is meaningless
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-swift-frontend -typecheck %s -enable-library-evolution -verify
2+
3+
public struct ResilientStruct : ~Copyable { // expected-error {{noncopyable struct 'ResilientStruct' must be @frozen in library evolution mode; non-@frozen public and @usableFromInline noncopyable types are not supported}}
4+
}
5+
6+
@frozen
7+
public struct FrozenStruct : ~Copyable {
8+
public init() {}
9+
}
10+
11+
@usableFromInline
12+
struct UsableFromInlineStruct : ~Copyable { // expected-error {{noncopyable struct 'UsableFromInlineStruct' must be @frozen in library evolution mode; non-@frozen public and @usableFromInline noncopyable types are not supported}}
13+
}
14+
15+
public enum ResilientEnum : ~Copyable { // expected-error {{noncopyable enum 'ResilientEnum' must be @frozen in library evolution mode; non-@frozen public and @usableFromInline noncopyable types are not supported}}
16+
}
17+
18+
@frozen
19+
public enum FrozenEnum : ~Copyable {
20+
}
21+
22+
@usableFromInline
23+
enum UsableFromInlineEnum : ~Copyable { // expected-error {{noncopyable enum 'UsableFromInlineEnum' must be @frozen in library evolution mode; non-@frozen public and @usableFromInline noncopyable types are not supported}}
24+
}
25+
26+
public class C {
27+
@usableFromInline
28+
var x: FrozenStruct
29+
30+
public init() {}
31+
32+
@inlinable
33+
convenience public init(delegating: ()) {
34+
self.init()
35+
x = FrozenStruct()
36+
}
37+
}

0 commit comments

Comments
 (0)