Skip to content

Commit 473e722

Browse files
authored
Merge pull request swiftlang#74744 from kavon/combo-fixes-6-26-2024
[6.0🍒] NCGenerics reverse condfail fix + bonus content!
2 parents e212e6e + 6a8cad9 commit 473e722

16 files changed

+224
-83
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
@@ -1912,6 +1912,10 @@ class ExtensionDecl final : public GenericContext, public Decl,
19121912
/// \endcode
19131913
bool isWrittenWithConstraints() const;
19141914

1915+
/// Does this extension add conformance to an invertible protocol for the
1916+
/// extended type?
1917+
bool isAddingConformanceToInvertible() const;
1918+
19151919
/// Returns the name of the category specified by the \c \@_objcImplementation
19161920
/// attribute, or \c None if the name is invalid or
19171921
/// \c isObjCImplementation() is false.

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7672,8 +7672,8 @@ ERROR(inverse_associatedtype_restriction, none,
76727672
"cannot suppress '%0' requirement of an associated type",
76737673
(StringRef))
76747674
ERROR(inverse_on_class, none,
7675-
"classes cannot be '~%0'",
7676-
(StringRef))
7675+
"%select{classes|actors}1 cannot be '~%0'",
7676+
(StringRef, bool))
76777677
ERROR(inverse_with_class_constraint, none,
76787678
"composition involving %select{class requirement %2|'AnyObject'}0 "
76797679
"cannot contain '~%1'",

include/swift/Basic/Features.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,8 @@ EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE(DebugDescriptionMacro, true)
387387

388388
EXPERIMENTAL_FEATURE(ReinitializeConsumeInMultiBlockDefer, false)
389389

390+
EXPERIMENTAL_FEATURE(SE427NoInferenceOnExtension, false)
391+
390392
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
391393
#undef EXPERIMENTAL_FEATURE
392394
#undef UPCOMING_FEATURE

lib/AST/ASTPrinter.cpp

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

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

16851685
void PrintAST::printGenericSignature(GenericSignature genericSig,
16861686
unsigned flags) {
1687+
assert(!((flags & InnermostOnly) && (flags & PrintInverseRequirements))
1688+
&& "InnermostOnly + PrintInverseRequirements is not handled");
1689+
16871690
printGenericSignature(genericSig, flags,
16881691
// print everything
1689-
[&](const Requirement &) { return true; });
1692+
[&](const Requirement &) { return true; },
1693+
AllInverses());
16901694
}
16911695

16921696
// Erase any requirements involving invertible protocols.
@@ -1704,16 +1708,35 @@ static void eraseInvertibleProtocolConformances(
17041708
});
17051709
}
17061710

1711+
InversesAtDepth::InversesAtDepth(GenericContext *level) {
1712+
includedDepth = std::nullopt;
1713+
// Does this generic context have its own generic parameters?
1714+
if (auto *list = level->getGenericParams()) {
1715+
includedDepth = list->getParams().back()->getDepth(); // use this depth.
1716+
}
1717+
}
1718+
bool InversesAtDepth::operator()(const InverseRequirement &inverse) const {
1719+
if (includedDepth) {
1720+
auto d = inverse.subject->castTo<GenericTypeParamType>()->getDepth();
1721+
return d == includedDepth.value();
1722+
}
1723+
return false;
1724+
}
1725+
17071726
void PrintAST::printGenericSignature(
17081727
GenericSignature genericSig,
17091728
unsigned flags,
1710-
llvm::function_ref<bool(const Requirement &)> filter) {
1729+
llvm::function_ref<bool(const Requirement &)> filter,
1730+
InverseFilter inverseFilter) {
17111731

17121732
SmallVector<Requirement, 2> requirements;
17131733
SmallVector<InverseRequirement, 2> inverses;
17141734

17151735
if (flags & PrintInverseRequirements) {
17161736
genericSig->getRequirementsWithInverses(requirements, inverses);
1737+
llvm::erase_if(inverses, [&](InverseRequirement inverse) -> bool {
1738+
return !inverseFilter(inverse);
1739+
});
17171740
} else {
17181741
requirements.append(genericSig.getRequirements().begin(),
17191742
genericSig.getRequirements().end());
@@ -1722,17 +1745,6 @@ void PrintAST::printGenericSignature(
17221745
eraseInvertibleProtocolConformances(requirements);
17231746
}
17241747

1725-
// Unless `IncludeOuterInverses` is enabled, limit inverses to the
1726-
// innermost generic parameters.
1727-
if (!(flags & IncludeOuterInverses) && !inverses.empty()) {
1728-
auto innerParams = genericSig.getInnermostGenericParams();
1729-
SmallPtrSet<TypeBase *, 4> innerParamSet(innerParams.begin(),
1730-
innerParams.end());
1731-
llvm::erase_if(inverses, [&](InverseRequirement inverse) -> bool {
1732-
return !innerParamSet.contains(inverse.subject.getPointer());
1733-
});
1734-
}
1735-
17361748
if (flags & InnermostOnly) {
17371749
auto genericParams = genericSig.getInnermostGenericParams();
17381750

@@ -2769,8 +2781,9 @@ void PrintAST::printDeclGenericRequirements(GenericContext *decl) {
27692781
// In many cases, inverses should not be printed for outer generic parameters.
27702782
// Exceptions to that include extensions, as it's valid to write an inverse
27712783
// on the generic parameters they get from the extended nominal.
2772-
if (isa<ExtensionDecl>(decl))
2773-
flags |= IncludeOuterInverses;
2784+
InverseFilter inverseFilter = AllInverses();
2785+
if (!isa<ExtensionDecl>(decl))
2786+
inverseFilter = InversesAtDepth(decl);
27742787

27752788
Printer.printStructurePre(PrintStructureKind::DeclGenericParameterClause);
27762789
printGenericSignature(genericSig,
@@ -2779,7 +2792,8 @@ void PrintAST::printDeclGenericRequirements(GenericContext *decl) {
27792792
if (parentSig)
27802793
return !parentSig->isRequirementSatisfied(req);
27812794
return true;
2782-
});
2795+
},
2796+
inverseFilter);
27832797
Printer.printStructurePost(PrintStructureKind::DeclGenericParameterClause);
27842798
}
27852799

@@ -2913,20 +2927,6 @@ void PrintAST::printExtendedTypeName(TypeLoc ExtendedTypeLoc) {
29132927
printTypeLoc(TypeLoc(ExtendedTypeLoc.getTypeRepr(), Ty));
29142928
}
29152929

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

3054-
auto genSigFlags = defaultGenericRequirementFlags()
3055-
| IncludeOuterInverses;
3054+
auto genSigFlags = defaultGenericRequirementFlags();
30563055

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

30663063
printGenericSignature(genericSig,
30673064
genSigFlags,
30683065
[baseGenericSig](const Requirement &req) -> bool {
30693066
// Only include constraints that are not satisfied by the base type.
30703067
return !baseGenericSig->isRequirementSatisfied(req);
3071-
});
3068+
},
3069+
AllInverses());
30723070
}
30733071
}
30743072
if (Options.TypeDefinitions) {

lib/AST/Decl.cpp

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

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

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ static bool usesFeatureGlobalActorIsolatedTypesUsability(Decl *decl) {
771771
}
772772

773773
UNINTERESTING_FEATURE(ReinitializeConsumeInMultiBlockDefer)
774+
UNINTERESTING_FEATURE(SE427NoInferenceOnExtension)
774775

775776
// ----------------------------------------------------------------------------
776777
// MARK: - FeatureSet

lib/Sema/TypeCheckDeclPrimary.cpp

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

33463346
ctx.Diags.diagnose(decl->getLoc(),
33473347
diag::inverse_on_class,
3348-
getProtocolName(getKnownProtocolKind(ip)));
3348+
getProtocolName(getKnownProtocolKind(ip)),
3349+
decl->isAnyActor());
33493350
}
33503351
}
33513352

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -785,33 +785,18 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
785785
} else if (auto *ext = dyn_cast<ExtensionDecl>(GC)) {
786786
loc = ext->getLoc();
787787

788-
// The inherited entries influence the generic signature of the extension,
789-
// because if it introduces conformance to invertible protocol IP, we do not
790-
// we do not infer any requirements that the generic parameters to conform
788+
// If the extension introduces conformance to invertible protocol IP, do not
789+
// infer any conditional requirements that the generic parameters to conform
791790
// to invertible protocols. This forces people to write out the conditions.
792-
const unsigned numEntries = ext->getInherited().size();
793-
for (unsigned i = 0; i < numEntries; ++i) {
794-
InheritedTypeRequest request{ext, i, TypeResolutionStage::Structural};
795-
auto result = evaluateOrDefault(ctx.evaluator, request,
796-
InheritedTypeResult::forDefault());
797-
Type inheritedTy;
798-
switch (result) {
799-
case InheritedTypeResult::Inherited:
800-
inheritedTy = result.getInheritedType();
801-
break;
802-
case InheritedTypeResult::Suppressed:
803-
case InheritedTypeResult::Default:
804-
continue;
805-
}
806-
807-
if (inheritedTy) {
808-
if (auto kp = inheritedTy->getKnownProtocol()) {
809-
if (getInvertibleProtocolKind(*kp)) {
810-
inferInvertibleReqs = false;
811-
break;
812-
}
813-
}
814-
}
791+
inferInvertibleReqs = !ext->isAddingConformanceToInvertible();
792+
793+
// FIXME: to workaround a reverse condfail, always infer the requirements if
794+
// the extension is in a swiftinterface file. This is temporary and should
795+
// be removed soon. (rdar://130424971)
796+
if (auto *sf = ext->getOutermostParentSourceFile()) {
797+
if (sf->Kind == SourceFileKind::Interface
798+
&& !ctx.LangOpts.hasFeature(Feature::SE427NoInferenceOnExtension))
799+
inferInvertibleReqs = true;
815800
}
816801

817802
collectAdditionalExtensionRequirements(ext->getExtendedType(), extraReqs);

0 commit comments

Comments
 (0)