Skip to content

Commit 091d63a

Browse files
committed
revise how the Copyable constraint is added
The _Copyable constraint was implemented as a marker protocol. That protocol is part of the KnownProtocol's in the compiler. When `ASTContext::getProtocol(KnownProtocolKind kind)` tries to find the ProtocolDecl for Copyable, it will look in the stdlib module (i.e., Swift module), which is where I initially planned to put it. That created problems initially when some regression tests use `-parse-stdlib` failed to do that protocol lookup, which is essential for adding the constraint (given the current implementation). That led to believe we need to pull Copyable out of the stdlib, but that's wrong. In fact, when building the Swift module itself, we do `-parse-stdlib` but we also include `-module-name Swift`. This causes the _Copyable protocol defined in the Stdlib to be correctly discovered while building the stdlib itself (see the test case in this commit). So, the only downside of having the Copyable protocol in the Stdlib is that `-parse-stdlib` tests in the compiler can't use move-only types correctly, as they'll be allowed in generic contexts. No real program would build like this. Until I have time to do a further refactoring, this is an acceptable trade-off. fixes rdar://104898230
1 parent 2f734e5 commit 091d63a

File tree

12 files changed

+141
-37
lines changed

12 files changed

+141
-37
lines changed

lib/AST/Module.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,9 +1768,12 @@ LookupConformanceInModuleRequest::evaluate(
17681768
if (nominal->isMoveOnly()) {
17691769
return ProtocolConformanceRef::forInvalid();
17701770
} else {
1771-
// FIXME: this should probably follow the Sendable case in that
1772-
// we should synthesize and append a ProtocolConformance to the `conformances` list.
1773-
return ProtocolConformanceRef(protocol);
1771+
// Specifically do not create a concrete conformance to Copyable. At
1772+
// this stage, we don't even want Copyable to appear in swiftinterface
1773+
// files, which will happen for a marker protocol that's registered
1774+
// in a nominal type's conformance table. We can reconsider this
1775+
// decision later once there's a clearer picture of noncopyable generics
1776+
return ProtocolConformanceRef(protocol);
17741777
}
17751778
} else {
17761779
// Was unable to infer the missing conformance.

lib/AST/ProtocolConformance.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,11 +1241,6 @@ static SmallVector<ProtocolConformance *, 2> findSynthesizedConformances(
12411241
// Concrete types may synthesize some conformances
12421242
if (!isa<ProtocolDecl>(nominal)) {
12431243
trySynthesize(KnownProtocolKind::Sendable);
1244-
1245-
// FIXME(kavon): make sure this conformance doesn't show up in swiftinterfaces
1246-
// before do this synthesis unconditionally.
1247-
if (dc->getASTContext().LangOpts.hasFeature(Feature::MoveOnly))
1248-
trySynthesize(KnownProtocolKind::Copyable);
12491244
}
12501245

12511246
/// Distributed actors can synthesize Encodable/Decodable, so look for those

lib/Sema/CSBindings.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,19 @@ void BindingSet::finalize(
501501

502502
if (TransitiveProtocols.has_value()) {
503503
for (auto *constraint : *TransitiveProtocols) {
504-
auto protocolTy = constraint->getSecondType();
504+
Type protocolTy = constraint->getSecondType();
505+
506+
// The Copyable protocol can't have members, yet will be a
507+
// constraint of basically all type variables, so don't suggest it.
508+
//
509+
// NOTE: worth considering for all marker protocols, but keep in
510+
// mind that you're allowed to extend them with members!
511+
if (auto p = protocolTy->getAs<ProtocolType>()) {
512+
if (ProtocolDecl *decl = p->getDecl())
513+
if (decl->isSpecificProtocol(KnownProtocolKind::Copyable))
514+
continue;
515+
}
516+
505517
addBinding({protocolTy, AllowedBindingKind::Exact, constraint});
506518
}
507519
}

lib/Sema/ConstraintSystem.cpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,17 +1828,12 @@ TypeVariableType *ConstraintSystem::openGenericParameter(
18281828
assert(result.second);
18291829
(void)result;
18301830

1831-
// When move-only types are available, add a constraint to force generic
1832-
// parameters to conform to a "Copyable" protocol.
1833-
if (getASTContext().LangOpts.hasFeature(Feature::MoveOnly)) {
1834-
ProtocolDecl *copyable = TypeChecker::getProtocol(
1835-
getASTContext(), SourceLoc(), KnownProtocolKind::Copyable);
1836-
1837-
// FIXME(kavon): there's a dependency ordering issues here with the
1838-
// protocol being defined in the stdlib, because when trying to build
1839-
// the stdlib itself, or a Swift program with -parse-stdlib, we can't
1840-
// load the protocol to add this constraint. (rdar://104898230)
1841-
assert(copyable && "stdlib is missing _Copyable protocol!");
1831+
// Add a constraint that generic parameters conform to Copyable.
1832+
// This lookup only can fail if the stdlib (i.e. the Swift module) has not
1833+
// been loaded because you've passed `-parse-stdlib` and are not building the
1834+
// stdlib itself (which would have `-module-name Swift` too).
1835+
if (auto *copyable = TypeChecker::getProtocol(getASTContext(), SourceLoc(),
1836+
KnownProtocolKind::Copyable)) {
18421837
addConstraint(
18431838
ConstraintKind::ConformsTo, typeVar,
18441839
copyable->getDeclaredInterfaceType(),

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4618,7 +4618,7 @@ swift::checkTypeWitness(Type type, AssociatedTypeDecl *assocType,
46184618
if (type->isPureMoveOnly()) {
46194619
// describe the failure reason as it not conforming to Copyable
46204620
auto *copyable = ctx.getProtocol(KnownProtocolKind::Copyable);
4621-
assert(copyable && "missing _Copyable from stdlib!");
4621+
assert(copyable && "missing _Copyable protocol!");
46224622
return CheckTypeWitnessResult(copyable->getDeclaredInterfaceType());
46234623
}
46244624

stdlib/public/core/Misc.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ public func _unsafePerformance<T>(_ c: () -> T) -> T {
155155
return c()
156156
}
157157

158-
159-
/// This is not a protocol you can explicitly use in your programs.
160-
/// It exists for the compiler and type checker for diagnostic purposes.
161-
@_marker public protocol _Copyable { }
158+
/// This marker protocol represents types that support copying.
159+
/// This type is not yet available for use to express explicit
160+
/// constraints on generics in your programs. It is currently
161+
/// only used internally by the compiler.
162+
@available(*, unavailable)
163+
@_marker public protocol _Copyable {}

test/Concurrency/async_overload_filtering.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func filter_async(_: String) -> Void {}
1717
var a: String? = nil
1818

1919
// CHECK: attempting disjunction choice $T0 bound to decl async_overload_filtering.(file).filter_async(fn2:)
20+
// CHECK-NEXT: added constraint: {{.*}} conforms to _Copyable
2021
// CHECK-NEXT: overload set choice binding $T0 := {{.*}}
2122
// CHECK-NEXT: (considering -> ({{.*}}) -> {{.*}} applicable fn {{.*}}
2223
// CHECK: increasing 'sync-in-asynchronous' score by 1

test/Constraints/moveonly_constraints.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ func checkCasting(_ b: any Box, _ mo: __shared MO, _ a: Any) {
150150

151151
let _: Sendable = (MO(), MO()) // expected-error {{move-only type '(MO, MO)' cannot be used with generics yet}}
152152
let _: Sendable = MO() // expected-error {{move-only type 'MO' cannot be used with generics yet}}
153-
let _: _Copyable = mo // expected-error {{move-only type 'MO' cannot be used with generics yet}}
153+
let _: _Copyable = mo // expected-error {{'_Copyable' is unavailable}}
154+
// expected-error@-1 {{move-only type 'MO' cannot be used with generics yet}}
154155
let _: AnyObject = MO() // expected-error {{move-only type 'MO' cannot be used with generics yet}}
155156
let _: Any = mo // expected-error {{move-only type 'MO' cannot be used with generics yet}}
156157

@@ -163,8 +164,6 @@ func checkCasting(_ b: any Box, _ mo: __shared MO, _ a: Any) {
163164
_ = a as MO // expected-error {{cannot convert value of type 'Any' to type 'MO' in coercion}}
164165
_ = b as MO // expected-error {{cannot convert value of type 'any Box' to type 'MO' in coercion}}
165166

166-
// FIXME(kavon): make sure at runtime these casts actually fail, or just make them errors? (rdar://104900293)
167-
168167
_ = MO() is AnyHashable // expected-warning {{cast from 'MO' to unrelated type 'AnyHashable' always fails}}
169168
// expected-error@-1 {{move-only types cannot be conditionally cast}}
170169
_ = MO() is AnyObject // expected-warning {{cast from 'MO' to unrelated type 'AnyObject' always fails}}

test/ModuleInterface/copyable.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// This test checks that conformances to _Copyable do not appear in swiftinterface files
4+
5+
// Generate the parseable interface of the current file via the merge-modules step
6+
// RUN: %target-build-swift -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/TestMerge.swiftinterface -module-name Test %s -enable-library-evolution -swift-version 5
7+
8+
// Generate the parseable interface of the current file via a single frontend invocation
9+
// RUN: %target-swift-frontend -enable-library-evolution -typecheck -emit-module-interface-path %t/TestSingle.swiftinterface -module-name Test %s -enable-library-evolution -swift-version 5
10+
11+
// Make sure Copyable doesn't appear anywhere in these files!
12+
// RUN: %FileCheck --implicit-check-not Copyable %s < %t/TestSingle.swiftinterface
13+
// RUN: %FileCheck --implicit-check-not Copyable %s < %t/TestMerge.swiftinterface
14+
15+
16+
// CHECK: forceGenericSubst
17+
public func forceGenericSubst<T>(_ t: T) {
18+
print(t)
19+
}
20+
21+
public protocol ProtocolWithAssocType {
22+
associatedtype SomeType
23+
func get() -> SomeType
24+
}
25+
26+
public class BestClass: ProtocolWithAssocType {
27+
public typealias SomeType = BestStruct
28+
public func get() -> SomeType { return BestStruct() }
29+
}
30+
31+
public struct BestStruct { let c = BestClass() }
32+
33+
public enum BestEnum<T> {
34+
case nothing
35+
case something(T)
36+
}
37+
38+
public func caller(_ c: BestClass, _ s: BestStruct, _ e: BestEnum<BestStruct>) {
39+
forceGenericSubst(c)
40+
forceGenericSubst(s)
41+
forceGenericSubst(e)
42+
}
43+
44+
public typealias TheTop = (Int, String)
45+
46+
public struct S<T> {
47+
let t: T
48+
init(_ t: T) { self.t = t }
49+
}
50+
51+
public typealias Handler = () -> ()
52+
53+
public func genericFn<T>(_ t: T) -> S<T> {
54+
return S(t)
55+
}
56+
57+
public func maker(_ top: TheTop, withCompletion comp: @escaping Handler) -> S<TheTop> {
58+
_ = genericFn(top)
59+
_ = genericFn(comp)
60+
return S(top)
61+
}

test/SILOptimizer/opaque_values_Onone_stdlib.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -parse-stdlib -module-name Swift -enable-sil-opaque-values -parse-as-library -emit-sil -Onone %s | %FileCheck %s
1+
// RUN: %target-swift-frontend -enable-experimental-move-only -parse-stdlib -module-name Swift -enable-sil-opaque-values -parse-as-library -emit-sil -Onone %s | %FileCheck %s
22

33
// Like opaque_values_Onone.swift but for code that needs to be compiled with
44
// -parse-stdlib.
@@ -10,6 +10,7 @@ precedencegroup AssignmentPrecedence { assignment: true }
1010
precedencegroup CastingPrecedence {}
1111

1212
public protocol _ObjectiveCBridgeable {}
13+
@_marker public protocol _Copyable {}
1314

1415
public protocol _ExpressibleByBuiltinBooleanLiteral {
1516
init(_builtinBooleanLiteral value: Builtin.Int1)
@@ -32,11 +33,10 @@ public func type<T, Metatype>(of value: T) -> Metatype
3233
class X {}
3334
func consume(_ x : __owned X) {}
3435

35-
// FIXME: disabled temporarily until rdar://104898230 is resolved
36-
//func foo(@_noImplicitCopy _ x: __owned X) {
37-
// consume(_copy(x))
38-
// consume(x)
39-
//}
36+
func foo(@_noImplicitCopy _ x: __owned X) {
37+
consume(_copy(x))
38+
consume(x)
39+
}
4040

4141
// CHECK-LABEL: sil [transparent] [_semantics "lifetimemanagement.copy"] @_copy : {{.*}} {
4242
// CHECK: {{bb[0-9]+}}([[OUT_ADDR:%[^,]+]] : $*T, [[IN_ADDR:%[^,]+]] : $*T):

0 commit comments

Comments
 (0)