Skip to content

Commit 0f95d26

Browse files
committed
NCGenerics: introduce SuppressedAssociatedTypes
The model for associated types hasn't been fully worked-out for noncopyable generics, but there is some support already that is being used by the stdlib for an internal-only (and rather cursed) protocol `_Pointer` to support `UnsafePointer`, etc. This patch gates the existing experimental support for associated types behind a feature flag. This flag doesn't emit feature-guards in interfaces, since support for it is tied closely to NoncopyableGenerics and has been there from its early days. (cherry picked from commit 3098353)
1 parent 36f31b9 commit 0f95d26

22 files changed

+130
-39
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7618,6 +7618,9 @@ ERROR(inverse_but_also_conforms, none,
76187618
ERROR(inverse_generic_but_also_conforms, none,
76197619
"%0 required to be '%1' but is marked with '~%1'",
76207620
(Type, StringRef))
7621+
ERROR(inverse_associatedtype_restriction, none,
7622+
"cannot suppress '%0' requirement of an associated type",
7623+
(StringRef))
76217624
ERROR(inverse_on_class, none,
76227625
"classes cannot be '~%0'",
76237626
(StringRef))

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ SUPPRESSIBLE_EXPERIMENTAL_FEATURE(NoncopyableGenerics, true)
322322
// Alias for NoncopyableGenerics
323323
EXPERIMENTAL_FEATURE(NoncopyableGenerics2, true)
324324

325+
// Enables ~Copyable and ~Escapable annotations on associatedtype declarations.
326+
EXPERIMENTAL_FEATURE(SuppressedAssociatedTypes, true)
327+
325328
/// Allow destructuring stored `let` bindings in structs.
326329
EXPERIMENTAL_FEATURE(StructLetDestructuring, true)
327330

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ static bool usesFeatureRawLayout(Decl *decl) {
504504
}
505505

506506
UNINTERESTING_FEATURE(Embedded)
507+
UNINTERESTING_FEATURE(SuppressedAssociatedTypes)
507508

508509
static bool usesFeatureNoncopyableGenerics(Decl *decl) {
509510
if (decl->getAttrs().hasAttribute<PreInverseGenericsAttr>())

lib/AST/RequirementMachine/Diagnostics.cpp

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -108,26 +108,22 @@ bool swift::rewriting::diagnoseRequirementErrors(
108108
}
109109

110110
case RequirementError::Kind::InvalidInverseSubject: {
111-
auto requirement = error.getRequirement();
112-
if (requirement.hasError())
113-
break;
114-
115-
assert(requirement.getKind() == RequirementKind::Conformance);
116-
117-
auto subjectType = requirement.getFirstType();
118-
auto constraintType = requirement.getSecondType();
119-
120-
assert(constraintType->is<ProtocolCompositionType>());
111+
auto inverse = error.getInverse();
112+
auto subjectType = inverse.subject;
113+
auto protoKind = getKnownProtocolKind(inverse.getKind());
121114

122-
// Pick one of the inverses to diagnose.
123-
auto inverses =
124-
constraintType->getAs<ProtocolCompositionType>()->getInverses();
125-
assert(!inverses.empty());
115+
StringRef name = getProtocolName(protoKind);
126116

127-
StringRef name = getProtocolName(getKnownProtocolKind(*inverses.begin()));
117+
if (subjectType->is<DependentMemberType>()) {
118+
// explain that associated types can't have inverses
119+
ctx.Diags.diagnose(loc, diag::inverse_associatedtype_restriction,
120+
name);
121+
} else {
122+
// generic diagnostic
123+
ctx.Diags.diagnose(loc, diag::requires_not_suitable_inverse_subject,
124+
subjectType, name);
125+
}
128126

129-
ctx.Diags.diagnose(loc, diag::requires_not_suitable_inverse_subject,
130-
subjectType, name);
131127
diagnosedError = true;
132128
break;
133129
}

lib/AST/RequirementMachine/Diagnostics.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,15 @@ struct RequirementError {
8484

8585
public:
8686
Requirement getRequirement() const {
87-
assert(!(kind == Kind::InvalidInverseOuterSubject ||
88-
kind == Kind::ConflictingInverseRequirement));
87+
assert(kind != Kind::InvalidInverseOuterSubject &&
88+
kind != Kind::InvalidInverseSubject &&
89+
kind != Kind::ConflictingInverseRequirement);
8990
return requirement;
9091
}
9192

9293
InverseRequirement getInverse() const {
9394
assert(kind == Kind::InvalidInverseOuterSubject ||
95+
kind == Kind::InvalidInverseSubject ||
9496
kind == Kind::ConflictingInverseRequirement);
9597
return inverse;
9698
}
@@ -107,14 +109,13 @@ struct RequirementError {
107109
return {Kind::InvalidRequirementSubject, req, loc};
108110
}
109111

110-
static RequirementError forInvalidInverseSubject(Requirement req,
111-
SourceLoc loc) {
112-
return {Kind::InvalidInverseSubject, req, loc};
112+
static RequirementError forInvalidInverseSubject(InverseRequirement inv) {
113+
return {Kind::InvalidInverseSubject, inv, inv.loc};
113114
}
114115

115116
static
116-
RequirementError forInvalidInverseOuterSubject(InverseRequirement req) {
117-
return {Kind::InvalidInverseOuterSubject, req, req.loc};
117+
RequirementError forInvalidInverseOuterSubject(InverseRequirement inv) {
118+
return {Kind::InvalidInverseOuterSubject, inv, inv.loc};
118119
}
119120

120121
static RequirementError forConflictingInverseRequirement(

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,8 @@ static void desugarConformanceRequirement(
378378
// Only permit type-parameter subjects.
379379
errors.push_back(
380380
RequirementError::forInvalidRequirementSubject(req, loc));
381-
} else if (subject->isParameterPack()) {
382-
// Disallow parameter packs for now.
383-
errors.push_back(RequirementError::forInvalidInverseSubject(req, loc));
384381
} else {
385-
// Record inverse.
382+
// Record and desugar inverses.
386383
auto &ctx = req.getFirstType()->getASTContext();
387384
for (auto ip : compositionType->getInverses())
388385
inverses.push_back({req.getFirstType(),
@@ -746,11 +743,30 @@ void swift::rewriting::applyInverses(
746743
SmallVectorImpl<StructuralRequirement> &result,
747744
SmallVectorImpl<RequirementError> &errors) {
748745

749-
// Summarize the inverses and flag ones that are incorrect.
746+
// No inverses to even validate.
747+
if (inverseList.empty())
748+
return;
749+
750+
const bool allowInverseOnAssocType =
751+
ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypes);
752+
753+
// Summarize the inverses and diagnose ones that are incorrect.
750754
llvm::DenseMap<CanType, InvertibleProtocolSet> inverses;
751755
for (auto inverse : inverseList) {
752756
auto canSubject = inverse.subject->getCanonicalType();
753757

758+
// Inverses on associated types are experimental.
759+
if (!allowInverseOnAssocType && canSubject->is<DependentMemberType>()) {
760+
errors.push_back(RequirementError::forInvalidInverseSubject(inverse));
761+
continue;
762+
}
763+
764+
// Noncopyable checking support for parameter packs is not implemented yet.
765+
if (canSubject->isParameterPack()) {
766+
errors.push_back(RequirementError::forInvalidInverseSubject(inverse));
767+
continue;
768+
}
769+
754770
// WARNING: possible quadratic behavior, but should be OK in practice.
755771
auto notInScope = llvm::none_of(gps, [=](Type t) {
756772
return t->getCanonicalType() == canSubject;

stdlib/cmake/modules/SwiftSource.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ function(_compile_swift_files
622622
endif()
623623

624624
list(APPEND swift_flags "-enable-experimental-feature" "NoncopyableGenerics2")
625+
list(APPEND swift_flags "-enable-experimental-feature" "SuppressedAssociatedTypes")
625626

626627
if(SWIFT_ENABLE_EXPERIMENTAL_NONESCAPABLE_TYPES)
627628
list(APPEND swift_flags "-enable-experimental-feature" "NonescapableTypes")
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %target-typecheck-verify-swift \
2+
// RUN: -enable-experimental-feature NoncopyableGenerics \
3+
// RUN: -enable-experimental-feature NonescapableTypes
4+
5+
// The restriction is that we don't permit suppression requirements on
6+
// associated types without an experimental feature for that.
7+
8+
protocol P {
9+
associatedtype A: ~Copyable // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
10+
}
11+
12+
protocol P_Prime: P {}
13+
14+
protocol Primary<T> {
15+
associatedtype T: ~Copyable // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
16+
}
17+
18+
// This is fine, since T isn't an associatedtype, it's substituted into one.
19+
typealias AliasPrimary<T> = Primary<T> where T: ~Copyable
20+
21+
protocol S {
22+
associatedtype One: ~Copyable // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
23+
24+
associatedtype Two: ~Escapable & ~Copyable
25+
// expected-error@-1 {{cannot suppress 'Copyable' requirement of an associated type}}
26+
// expected-error@-2 {{cannot suppress 'Escapable' requirement of an associated type}}
27+
28+
associatedtype Three: ~Escapable // expected-error {{cannot suppress 'Escapable' requirement of an associated type}}
29+
}
30+
31+
protocol Base {
32+
associatedtype A
33+
}
34+
protocol Derived: Base where Self.A: ~Copyable {} // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
35+
36+
protocol Q {
37+
associatedtype A where A: ~Copyable // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
38+
}
39+
40+
protocol R where Self.A: ~Copyable { // expected-error {{cannot suppress 'Copyable' requirement of an associated type}}
41+
associatedtype A
42+
}

test/Generics/inverse_copyable_requirement_newer_errors.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ func packIt() {
1717
func packingUniqueHeat_1<each T: ~Copyable>(_ t: repeat each T) {}
1818
// expected-error@-1{{cannot suppress '~Copyable' on type 'each T'}}
1919
// expected-note@-2{{generic parameter 'each T' has an implicit Copyable requirement}}
20+
// expected-error@-3{{'each T' required to be 'Copyable' but is marked with '~Copyable'}}
2021

2122
func packingUniqueHeat_2<each T>(_ t: repeat each T)
2223
where repeat each T: ~Copyable {}
2324
// expected-error@-1{{cannot suppress '~Copyable' on type 'each T'}}
2425
// expected-note@-3{{generic parameter 'each T' has an implicit Copyable requirement}}
26+
// expected-error@-3{{'each T' required to be 'Copyable' but is marked with '~Copyable'}}
2527

2628
func packItUniquely() {
2729
packingUniqueHeat_1(MO())

test/Generics/inverse_extensions.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %target-typecheck-verify-swift \
22
// RUN: -enable-experimental-feature NoncopyableGenerics \
3-
// RUN: -enable-experimental-feature NonescapableTypes
3+
// RUN: -enable-experimental-feature NonescapableTypes \
4+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes
45

56
struct Turtle<T> {}
67
extension Turtle where T: ~Copyable {} // expected-error {{'T' required to be 'Copyable' but is marked with '~Copyable'}}

test/Generics/inverse_generics.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes
1+
// RUN: %target-typecheck-verify-swift \
2+
// RUN: -enable-experimental-feature NoncopyableGenerics \
3+
// RUN: -enable-experimental-feature NonescapableTypes \
4+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes
25

36

47

test/Generics/inverse_scoping.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes -enable-experimental-feature SuppressedAssociatedTypes
22

33

44

test/Generics/inverse_signatures.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
// RUN: %target-swift-frontend -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes -verify -typecheck %s -debug-generic-signatures -debug-inverse-requirements 2>&1 | %FileCheck %s --implicit-check-not "error:"
1+
// RUN: %target-swift-frontend \
2+
// RUN: -enable-experimental-feature NoncopyableGenerics \
3+
// RUN: -enable-experimental-feature NonescapableTypes \
4+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
5+
// RUN: -verify -typecheck %s -debug-generic-signatures \
6+
// RUN: -debug-inverse-requirements 2>&1 | %FileCheck %s --implicit-check-not "error:"
27

38
// CHECK-LABEL: (file).genericFn@
49
// CHECK: Generic signature: <T where T : Copyable, T : Escapable>

test/IRGen/existential_shape_metadata_noncopyable.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
// RUN: %target-swift-frontend -emit-ir %s -swift-version 5 -disable-availability-checking -enable-experimental-feature NoncopyableGenerics -module-name existential_shape_metadata | %IRGenFileCheck %s
1+
// RUN: %target-swift-frontend \
2+
// RUN: -emit-ir %s -swift-version 5 \
3+
// RUN: -disable-availability-checking \
4+
// RUN: -enable-experimental-feature NoncopyableGenerics \
5+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
6+
// RUN: -module-name existential_shape_metadata | %IRGenFileCheck %s
27

38
// NOTE: Once noncopyable generics are enabled by default, merge this back into existential_shape_metadata.swift
49

test/IRGen/mangling_inverse_generics_evolution.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-swift-frontend -emit-ir -o - %s -module-name test \
33
// RUN: -enable-experimental-feature NoncopyableGenerics \
4+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
45
// RUN: -enable-experimental-feature NonescapableTypes \
56
// RUN: -parse-as-library \
67
// RUN: -enable-library-evolution \

test/Interpreter/moveonly_generics_associatedtype.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// RUN: %target-swift-emit-sil %s -DBAD_COPY -verify -sil-verify-all -enable-experimental-feature NoncopyableGenerics
2-
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics) | %FileCheck %s
3-
// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics) | %FileCheck %s
1+
// RUN: %target-swift-emit-sil %s -DBAD_COPY -verify -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes
2+
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes) | %FileCheck %s
3+
// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes) | %FileCheck %s
44

55
// REQUIRES: executable_test
66
// REQUIRES: asserts

test/ModuleInterface/invertible_constraints.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name Test -enable-experimental-feature NoncopyableGenerics
2+
// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name Test -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes
33
// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name Test
44
// RUN: %FileCheck %s < %t.swiftinterface
55

test/ModuleInterface/noncopyable_generics.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -emit-module \
44
// RUN: -enable-experimental-feature NoncopyableGenerics \
5+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
56
// RUN: -enable-experimental-feature NonescapableTypes \
67
// RUN: -o %t/NoncopyableGenerics_Misc.swiftmodule \
78
// RUN: -emit-module-interface-path %t/NoncopyableGenerics_Misc.swiftinterface \
89
// RUN: %S/Inputs/NoncopyableGenerics_Misc.swift
910

1011
// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -emit-module \
1112
// RUN: -enable-experimental-feature NoncopyableGenerics \
13+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
1214
// RUN: -enable-experimental-feature NonescapableTypes \
1315
// RUN: -enable-experimental-feature BorrowingSwitch \
1416
// RUN: -o %t/Swiftskell.swiftmodule \
@@ -24,16 +26,19 @@
2426

2527
// RUN: %target-swift-frontend -compile-module-from-interface \
2628
// RUN: -enable-experimental-feature NoncopyableGenerics \
29+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
2730
// RUN: -enable-experimental-feature NonescapableTypes \
2831
// RUN: %t/NoncopyableGenerics_Misc.swiftinterface -o %t/NoncopyableGenerics_Misc.swiftmodule
2932

3033
// RUN: %target-swift-frontend -compile-module-from-interface \
3134
// RUN: -enable-experimental-feature NoncopyableGenerics \
35+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
3236
// RUN: -enable-experimental-feature NonescapableTypes \
3337
// RUN: %t/Swiftskell.swiftinterface -o %t/Swiftskell.swiftmodule
3438

3539
// RUN: %target-swift-frontend -emit-silgen -I %t %s \
3640
// RUN: -enable-experimental-feature NoncopyableGenerics \
41+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
3742
// RUN: -enable-experimental-feature NonescapableTypes \
3843
// RUN: -o %t/final.silgen
3944

test/Parse/inverses.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes
22

33
protocol U {}
44

@@ -58,6 +58,7 @@ struct S: ~U, // expected-error {{type 'U' cannot be suppressed}}
5858
~Copyable {}
5959

6060
func greenBay<each T: ~Copyable>(_ r: repeat each T) {} // expected-error{{cannot suppress '~Copyable' on type 'each T'}}
61+
// expected-error@-1 {{'each T' required to be 'Copyable' but is marked with '~Copyable'}}
6162

6263
typealias Clone = Copyable
6364
func dup<D: ~Clone>(_ d: D) {}
@@ -106,6 +107,7 @@ struct NotAProtocol {}
106107

107108
struct Bad: ~NotAProtocol {} // expected-error {{type 'NotAProtocol' cannot be suppressed}}
108109

110+
109111
struct X<T: ~Copyable>: ~Copyable { }
110112

111113
func typeInExpression() {

test/SIL/lifetime_dependence_generics.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %target-swift-frontend %s -emit-sil \
22
// RUN: -enable-experimental-feature NonescapableTypes \
3+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
34
// RUN: -enable-experimental-feature NoncopyableGenerics | %FileCheck %s
45

56

test/SILOptimizer/lifetime_dependence_generic.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// RUN: -sil-verify-all \
55
// RUN: -enable-experimental-feature NonescapableTypes \
66
// RUN: -enable-experimental-feature NoncopyableGenerics \
7+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
78
// RUN: -parse-stdlib -module-name Swift
89

910
// REQUIRES: asserts

test/Serialization/noncopyable_generics.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-swift-frontend %S/Inputs/ncgenerics.swift \
33
// RUN: -enable-experimental-feature NoncopyableGenerics \
4+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
45
// RUN: -emit-module -module-name ncgenerics \
56
// RUN: -o %t
67

@@ -11,6 +12,7 @@
1112

1213
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) \
1314
// RUN: -enable-experimental-feature NoncopyableGenerics \
15+
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
1416
// RUN: -print-module -module-to-print=ncgenerics \
1517
// RUN: -I %t -source-filename=%s \
1618
// RUN: | %FileCheck -check-prefix=CHECK-PRINT %s

0 commit comments

Comments
 (0)