Skip to content

Commit 4b98498

Browse files
authored
Merge pull request #74672 from kavon/post-vacation-misc-cleanups
Handful of small NoncopyableGenerics fixes / cleanups.
2 parents 0c0a4a0 + bcedcca commit 4b98498

File tree

13 files changed

+173
-84
lines changed

13 files changed

+173
-84
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,46 @@ More generally, multiple availabilities can be specified, like so:
917917
enum Toast { ... }
918918
```
919919

920+
## `@_preInverseGenerics`
921+
922+
By default when mangling a generic signature, the presence of a conformance
923+
requirement for an invertible protocol, like Copyable and Escapable, is not
924+
explicitly mangled. Only the _absence_ of those conformance requirements for
925+
each generic parameter appears in the mangled name.
926+
927+
This attribute changes the way generic signatures are mangled, by ignoring
928+
even the absences of those conformance requirements for invertible protocols.
929+
So, the following functions would have the same mangling because of the
930+
attribute:
931+
932+
```swift
933+
@_preInverseGenerics
934+
func foo<T: ~Copyable>(_ t: borrowing T) {}
935+
936+
// In 'bug.swift', the function above without the attribute would be:
937+
//
938+
// $s3bug3fooyyxRi_zlF ---> bug.foo<A where A: ~Swift.Copyable>(A) -> ()
939+
//
940+
// With the attribute, the above becomes:
941+
//
942+
// $s3bug3fooyyxlF ---> bug.foo<A>(A) -> ()
943+
//
944+
// which is exactly the same symbol for the function below.
945+
946+
func foo<T>(_ t: T) {}
947+
```
948+
949+
The purpose of this attribute is to aid in adopting noncopyable generics
950+
(SE-427) in existing libraries without breaking ABI; it is for advanced users
951+
only.
952+
953+
> **WARNING:** Before applying this attribute, you _must manually verify_ that
954+
> there never were any implementations of `foo` that contained a copy of `t`,
955+
> to ensure correctness. There is no way to prove this by simply inspecting the
956+
> Swift source code! You actually have to **check the assembly code** in all of
957+
> your existing libraries containing `foo`, because an older version of the
958+
> Swift compiler could have decided to insert a copy of `t` as an optimization!
959+
920960
## `@_private(sourceFile: "FileName.swift")`
921961

922962
Fully bypasses access control, allowing access to private declarations

include/swift/AST/ASTPrinter.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,29 @@ enum class PrintStructureKind {
107107
FunctionParameterType,
108108
};
109109

110+
/// ---------------------------------
111+
/// MARK: inverse filtering functors
112+
113+
/// An inverse filter is just a function-object. Use one of the functors below
114+
/// to create such a filter.
115+
using InverseFilter = std::function<bool(const InverseRequirement &)>;
116+
117+
/// Include all of them!
118+
class AllInverses {
119+
public:
120+
bool operator()(const InverseRequirement &) const { return true; }
121+
};
122+
123+
/// Only prints inverses on generic parameters defined in the specified
124+
/// generic context.
125+
class InversesAtDepth {
126+
std::optional<unsigned> includedDepth;
127+
public:
128+
InversesAtDepth(GenericContext *level);
129+
bool operator()(const InverseRequirement &) const;
130+
};
131+
/// ---------------------------------
132+
110133
/// An abstract class used to print an AST.
111134
class ASTPrinter {
112135
unsigned CurrentIndentation = 0;

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,6 +1919,10 @@ class ExtensionDecl final : public GenericContext, public Decl,
19191919
/// \endcode
19201920
bool isWrittenWithConstraints() const;
19211921

1922+
/// Does this extension add conformance to an invertible protocol for the
1923+
/// extended type?
1924+
bool isAddingConformanceToInvertible() const;
1925+
19221926
/// If this extension represents an imported Objective-C category, returns the
19231927
/// category's name. Otherwise returns the empty identifier.
19241928
Identifier getObjCCategoryName() const;

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7719,8 +7719,8 @@ ERROR(inverse_associatedtype_restriction, none,
77197719
"cannot suppress '%0' requirement of an associated type",
77207720
(StringRef))
77217721
ERROR(inverse_on_class, none,
7722-
"classes cannot be '~%0'",
7723-
(StringRef))
7722+
"%select{classes|actors}1 cannot be '~%0'",
7723+
(StringRef, bool))
77247724
ERROR(inverse_with_class_constraint, none,
77257725
"composition involving %select{class requirement %2|'AnyObject'}0 "
77267726
"cannot contain '~%1'",

lib/AST/ASTPrinter.cpp

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,6 @@ class PrintAST : public ASTVisitor<PrintAST> {
974974
SwapSelfAndDependentMemberType = 8,
975975
PrintInherited = 16,
976976
PrintInverseRequirements = 32,
977-
IncludeOuterInverses = 64,
978977
};
979978

980979
/// The default generic signature flags for printing requirements.
@@ -1004,7 +1003,8 @@ class PrintAST : public ASTVisitor<PrintAST> {
10041003
void
10051004
printGenericSignature(GenericSignature genericSig,
10061005
unsigned flags,
1007-
llvm::function_ref<bool(const Requirement &)> filter);
1006+
llvm::function_ref<bool(const Requirement &)> filter,
1007+
InverseFilter inverseFilter);
10081008
void printSingleDepthOfGenericSignature(
10091009
ArrayRef<GenericTypeParamType *> genericParams,
10101010
ArrayRef<Requirement> requirements,
@@ -1693,9 +1693,13 @@ static unsigned getDepthOfRequirement(const Requirement &req) {
16931693

16941694
void PrintAST::printGenericSignature(GenericSignature genericSig,
16951695
unsigned flags) {
1696+
ASSERT(!((flags & InnermostOnly) && (flags & PrintInverseRequirements))
1697+
&& "InnermostOnly + PrintInverseRequirements is not handled");
1698+
16961699
printGenericSignature(genericSig, flags,
16971700
// print everything
1698-
[&](const Requirement &) { return true; });
1701+
[&](const Requirement &) { return true; },
1702+
AllInverses());
16991703
}
17001704

17011705
// Erase any requirements involving invertible protocols.
@@ -1710,16 +1714,35 @@ static void eraseInvertibleProtocolConformances(
17101714
});
17111715
}
17121716

1717+
InversesAtDepth::InversesAtDepth(GenericContext *level) {
1718+
includedDepth = std::nullopt;
1719+
// Does this generic context have its own generic parameters?
1720+
if (auto *list = level->getGenericParams()) {
1721+
includedDepth = list->getParams().back()->getDepth(); // use this depth.
1722+
}
1723+
}
1724+
bool InversesAtDepth::operator()(const InverseRequirement &inverse) const {
1725+
if (includedDepth) {
1726+
auto d = inverse.subject->castTo<GenericTypeParamType>()->getDepth();
1727+
return d == includedDepth.value();
1728+
}
1729+
return false;
1730+
}
1731+
17131732
void PrintAST::printGenericSignature(
17141733
GenericSignature genericSig,
17151734
unsigned flags,
1716-
llvm::function_ref<bool(const Requirement &)> filter) {
1735+
llvm::function_ref<bool(const Requirement &)> filter,
1736+
InverseFilter inverseFilter) {
17171737

17181738
SmallVector<Requirement, 2> requirements;
17191739
SmallVector<InverseRequirement, 2> inverses;
17201740

17211741
if (flags & PrintInverseRequirements) {
17221742
genericSig->getRequirementsWithInverses(requirements, inverses);
1743+
llvm::erase_if(inverses, [&](InverseRequirement inverse) -> bool {
1744+
return !inverseFilter(inverse);
1745+
});
17231746
} else {
17241747
requirements.append(genericSig.getRequirements().begin(),
17251748
genericSig.getRequirements().end());
@@ -1728,17 +1751,6 @@ void PrintAST::printGenericSignature(
17281751
eraseInvertibleProtocolConformances(requirements);
17291752
}
17301753

1731-
// Unless `IncludeOuterInverses` is enabled, limit inverses to the
1732-
// innermost generic parameters.
1733-
if (!(flags & IncludeOuterInverses) && !inverses.empty()) {
1734-
auto innerParams = genericSig.getInnermostGenericParams();
1735-
SmallPtrSet<TypeBase *, 4> innerParamSet(innerParams.begin(),
1736-
innerParams.end());
1737-
llvm::erase_if(inverses, [&](InverseRequirement inverse) -> bool {
1738-
return !innerParamSet.contains(inverse.subject.getPointer());
1739-
});
1740-
}
1741-
17421754
if (flags & InnermostOnly) {
17431755
auto genericParams = genericSig.getInnermostGenericParams();
17441756

@@ -2772,8 +2784,9 @@ void PrintAST::printDeclGenericRequirements(GenericContext *decl) {
27722784
// In many cases, inverses should not be printed for outer generic parameters.
27732785
// Exceptions to that include extensions, as it's valid to write an inverse
27742786
// on the generic parameters they get from the extended nominal.
2775-
if (isa<ExtensionDecl>(decl))
2776-
flags |= IncludeOuterInverses;
2787+
InverseFilter inverseFilter = AllInverses();
2788+
if (!isa<ExtensionDecl>(decl))
2789+
inverseFilter = InversesAtDepth(decl);
27772790

27782791
Printer.printStructurePre(PrintStructureKind::DeclGenericParameterClause);
27792792
printGenericSignature(genericSig,
@@ -2782,7 +2795,8 @@ void PrintAST::printDeclGenericRequirements(GenericContext *decl) {
27822795
if (parentSig)
27832796
return !parentSig->isRequirementSatisfied(req);
27842797
return true;
2785-
});
2798+
},
2799+
inverseFilter);
27862800
Printer.printStructurePost(PrintStructureKind::DeclGenericParameterClause);
27872801
}
27882802

@@ -2916,20 +2930,6 @@ void PrintAST::printExtendedTypeName(TypeLoc ExtendedTypeLoc) {
29162930
printTypeLoc(TypeLoc(ExtendedTypeLoc.getTypeRepr(), Ty));
29172931
}
29182932

2919-
/// If an extension adds a conformance for an invertible protocol, then we
2920-
/// should not print inverses for its generic signature, because no conditional
2921-
/// requirements are inferred by default for such an extension.
2922-
static bool isExtensionAddingInvertibleConformance(const ExtensionDecl *ext) {
2923-
auto conformances = ext->getLocalConformances();
2924-
for (auto *conf : conformances) {
2925-
if (conf->getProtocol()->getInvertibleProtocolKind()) {
2926-
assert(conformances.size() == 1 && "expected solo conformance");
2927-
return true;
2928-
}
2929-
}
2930-
return false;
2931-
}
2932-
29332933
void PrintAST::printSynthesizedExtension(Type ExtendedType,
29342934
ExtensionDecl *ExtDecl) {
29352935
if (Options.PrintCompatibilityFeatureChecks &&
@@ -3054,24 +3054,22 @@ void PrintAST::printExtension(ExtensionDecl *decl) {
30543054
assert(baseGenericSig &&
30553055
"an extension can't be generic if the base type isn't");
30563056

3057-
auto genSigFlags = defaultGenericRequirementFlags()
3058-
| IncludeOuterInverses;
3057+
auto genSigFlags = defaultGenericRequirementFlags();
30593058

30603059
// Disable printing inverses if the extension is adding a conformance
30613060
// for an invertible protocol itself, as we do not infer any requirements
30623061
// in such an extension. We need to print the whole signature:
30633062
// extension S: Copyable where T: Copyable
3064-
if (isExtensionAddingInvertibleConformance(decl)) {
3063+
if (decl->isAddingConformanceToInvertible())
30653064
genSigFlags &= ~PrintInverseRequirements;
3066-
genSigFlags &= ~IncludeOuterInverses;
3067-
}
30683065

30693066
printGenericSignature(genericSig,
30703067
genSigFlags,
30713068
[baseGenericSig](const Requirement &req) -> bool {
30723069
// Only include constraints that are not satisfied by the base type.
30733070
return !baseGenericSig->isRequirementSatisfied(req);
3074-
});
3071+
},
3072+
AllInverses());
30753073
}
30763074
}
30773075
if (Options.TypeDefinitions) {

lib/AST/Decl.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,6 +1892,30 @@ Type ExtensionDecl::getExtendedType() const {
18921892
return ErrorType::get(ctx);
18931893
}
18941894

1895+
bool ExtensionDecl::isAddingConformanceToInvertible() const {
1896+
const unsigned numEntries = getInherited().size();
1897+
for (unsigned i = 0; i < numEntries; ++i) {
1898+
InheritedTypeRequest request{this, i, TypeResolutionStage::Structural};
1899+
auto result = evaluateOrDefault(getASTContext().evaluator, request,
1900+
InheritedTypeResult::forDefault());
1901+
Type inheritedTy;
1902+
switch (result) {
1903+
case InheritedTypeResult::Inherited:
1904+
inheritedTy = result.getInheritedType();
1905+
break;
1906+
case InheritedTypeResult::Suppressed:
1907+
case InheritedTypeResult::Default:
1908+
continue;
1909+
}
1910+
1911+
if (inheritedTy)
1912+
if (auto kp = inheritedTy->getKnownProtocol())
1913+
if (getInvertibleProtocolKind(*kp))
1914+
return true;
1915+
}
1916+
return false;
1917+
}
1918+
18951919
bool Decl::isObjCImplementation() const {
18961920
return getAttrs().hasAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
18971921
}

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3349,7 +3349,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
33493349

33503350
ctx.Diags.diagnose(decl->getLoc(),
33513351
diag::inverse_on_class,
3352-
getProtocolName(getKnownProtocolKind(ip)));
3352+
getProtocolName(getKnownProtocolKind(ip)),
3353+
decl->isAnyActor());
33533354
}
33543355
}
33553356

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -783,34 +783,10 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
783783
} else if (auto *ext = dyn_cast<ExtensionDecl>(GC)) {
784784
loc = ext->getLoc();
785785

786-
// The inherited entries influence the generic signature of the extension,
787-
// because if it introduces conformance to invertible protocol IP, we do not
788-
// we do not infer any requirements that the generic parameters to conform
786+
// If the extension introduces conformance to invertible protocol IP, do not
787+
// infer any conditional requirements that the generic parameters to conform
789788
// to invertible protocols. This forces people to write out the conditions.
790-
const unsigned numEntries = ext->getInherited().size();
791-
for (unsigned i = 0; i < numEntries; ++i) {
792-
InheritedTypeRequest request{ext, i, TypeResolutionStage::Structural};
793-
auto result = evaluateOrDefault(ctx.evaluator, request,
794-
InheritedTypeResult::forDefault());
795-
Type inheritedTy;
796-
switch (result) {
797-
case InheritedTypeResult::Inherited:
798-
inheritedTy = result.getInheritedType();
799-
break;
800-
case InheritedTypeResult::Suppressed:
801-
case InheritedTypeResult::Default:
802-
continue;
803-
}
804-
805-
if (inheritedTy) {
806-
if (auto kp = inheritedTy->getKnownProtocol()) {
807-
if (getInvertibleProtocolKind(*kp)) {
808-
inferInvertibleReqs = false;
809-
break;
810-
}
811-
}
812-
}
813-
}
789+
inferInvertibleReqs = !ext->isAddingConformanceToInvertible();
814790

815791
collectAdditionalExtensionRequirements(ext->getExtendedType(), extraReqs);
816792

test/Generics/inverse_classes2.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
// RUN: %target-typecheck-verify-swift \
2-
// RUN: -parse-stdlib -module-name Swift
3-
4-
// NOTE: -parse-stdlib is a transitional workaround and should not be required.
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking
52

63
@_moveOnly // expected-error {{'@_moveOnly' attribute is only valid on structs or enums}}
74
class KlassLegacy {}
85

96
class KlassModern: ~Copyable {} // expected-error {{classes cannot be '~Copyable'}}
107

8+
actor FamousPerson: ~Copyable {} // expected-error{{actors cannot be '~Copyable'}}
9+
1110
class Konditional<T: ~Copyable> {}
1211

1312
func checks<T: ~Copyable, C>(

test/ModuleInterface/Inputs/NoncopyableGenerics_Misc.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,8 @@ public struct Generic<T: Publik & ~Copyable> : (P & ~Copyable) {}
137137
public struct VeryNested: (P & (Q & ~Copyable & Publik) & (P & ~Copyable)) {}
138138
public struct Twice: P & ~Copyable, Q & ~Copyable {}
139139
public struct RegularTwice: ~Copyable, ~Copyable {}
140+
141+
// coverage for rdar://130179698
142+
public struct Continuation<T: ~Copyable, E: Error> {
143+
public func resume(returning value: consuming T) where E == Never {}
144+
}

test/ModuleInterface/noncopyable_generics.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@ import NoncopyableGenerics_Misc
215215
// CHECK-MISC-NEXT: public struct RegularTwice : ~Swift.Copyable, ~Swift.Copyable {
216216
// CHECK-MISC-NEXT: }
217217

218+
// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NoncopyableGenerics
219+
// CHECK-MISC-NEXT: public struct Continuation<T, E> where E : Swift.Error, T : ~Copyable {
220+
// CHECK-MISC-NOT: ~
221+
// CHECK-MISC: #endif
222+
218223
// NOTE: below are extensions emitted at the end of NoncopyableGenerics_Misc.swift
219224
// CHECK-MISC: extension {{.*}}.VeryNested : {{.*}}.Publik {}
220225

0 commit comments

Comments
 (0)