Skip to content

Commit d0938a9

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
1 parent b8a4132 commit d0938a9

13 files changed

+135
-15
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7301,6 +7301,11 @@ ERROR(noimplicitcopy_attr_invalid_in_generic_context,
73017301
none, "'@_noImplicitCopy' attribute cannot be applied to entities in generic contexts", ())
73027302
ERROR(noimplicitcopy_attr_not_allowed_on_moveonlytype,none,
73037303
"'@_noImplicitCopy' has no effect when applied to a noncopyable type", ())
7304+
ERROR(noncopyable_types_cannot_be_resilient, none,
7305+
"noncopyable %0 %1 must be @frozen in library evolution mode; "
7306+
"non-@frozen public and @usableFromInline noncopyable types are not "
7307+
"supported",
7308+
(DescriptiveDeclKind, Identifier))
73047309

73057310
//------------------------------------------------------------------------------
73067311
// MARK: Runtime discoverable attributes (@runtimeMetadata)

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ EXPERIMENTAL_FEATURE(NoImplicitCopy, true)
133133
EXPERIMENTAL_FEATURE(OldOwnershipOperatorSpellings, true)
134134
EXPERIMENTAL_FEATURE(MoveOnlyEnumDeinits, true)
135135
EXPERIMENTAL_FEATURE(MoveOnlyTuples, true)
136+
EXPERIMENTAL_FEATURE(MoveOnlyResilientTypes, true)
136137

137138
EXPERIMENTAL_FEATURE(OneWayClosureParameters, false)
138139
EXPERIMENTAL_FEATURE(TypeWitnessSystemInference, false)

lib/AST/ASTPrinter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3304,6 +3304,12 @@ static bool usesFeatureMoveOnlyEnumDeinits(Decl *decl) {
33043304
return false;
33053305
}
33063306

3307+
static bool usesFeatureMoveOnlyResilientTypes(Decl *decl) {
3308+
if (auto *nomDecl = dyn_cast<NominalTypeDecl>(decl))
3309+
return nomDecl->isResilient() && usesFeatureMoveOnly(decl);
3310+
return false;
3311+
}
3312+
33073313
static bool usesFeatureOneWayClosureParameters(Decl *decl) {
33083314
return false;
33093315
}

lib/Sema/TypeCheckDeclPrimary.cpp

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

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

@@ -2721,6 +2728,13 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
27212728
diagnoseCopyableTypeContainingMoveOnlyType(SD);
27222729

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

27262740
/// 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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %target-swift-emit-silgen -enable-experimental-feature NoImplicitCopy -enable-library-evolution %s | %FileCheck %s
2-
// RUN: %target-swift-emit-sil -O -sil-verify-all -enable-experimental-feature NoImplicitCopy -enable-library-evolution %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
33

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

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/SILOptimizer/moveonly_deinit_devirtualization_library_evolution.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -enable-library-evolution -module-name main -enable-sil-verify-all -sil-move-only-deinit-devirtualization -enable-experimental-feature MoveOnlyClasses -enable-experimental-feature MoveOnlyEnumDeinits %s | %FileCheck %s
1+
// RUN: %target-sil-opt -enable-library-evolution -module-name main -enable-sil-verify-all -sil-move-only-deinit-devirtualization -enable-experimental-feature MoveOnlyClasses -enable-experimental-feature MoveOnlyEnumDeinits -enable-experimental-feature MoveOnlyResilientTypes %s | %FileCheck %s
22

33
sil_stage raw
44

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)