Skip to content

Commit 4ac34a4

Browse files
@retroactive conformance syntax and checking (#36068)
1 parent f1bec99 commit 4ac34a4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+493
-65
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ TYPE_ATTR(differentiable)
5555
TYPE_ATTR(noDerivative)
5656
TYPE_ATTR(async)
5757
TYPE_ATTR(Sendable)
58+
TYPE_ATTR(retroactive)
5859
TYPE_ATTR(unchecked)
5960
TYPE_ATTR(_local)
6061
TYPE_ATTR(_noMetadata)

include/swift/AST/CASTBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ typedef enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedTypeAttrKind : size_t {
154154
BridgedTypeAttrKind_noDerivative,
155155
BridgedTypeAttrKind_async,
156156
BridgedTypeAttrKind_Sendable,
157+
BridgedTypeAttrKind_retroactive,
157158
BridgedTypeAttrKind_unchecked,
158159
BridgedTypeAttrKind__local,
159160
BridgedTypeAttrKind__noMetadata,

include/swift/AST/Decl.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,10 +1573,14 @@ struct InheritedEntry : public TypeLoc {
15731573
/// Whether there was an @unchecked attribute.
15741574
bool isUnchecked = false;
15751575

1576+
/// Whether there was an @retroactive attribute.
1577+
bool isRetroactive = false;
1578+
15761579
InheritedEntry(const TypeLoc &typeLoc);
15771580

1578-
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked)
1579-
: TypeLoc(typeLoc), isUnchecked(isUnchecked) { }
1581+
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive)
1582+
: TypeLoc(typeLoc), isUnchecked(isUnchecked), isRetroactive(isRetroactive) {
1583+
}
15801584
};
15811585

15821586
/// A wrapper for the collection of inherited types for either a `TypeDecl` or

include/swift/AST/DiagnosticsSema.def

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,6 +2454,23 @@ NOTE(invalid_extension_rewrite,none,
24542454
ERROR(synthesized_nominal_extension,none,
24552455
"cannot extend synthesized type %0", (Type))
24562456

2457+
ERROR(retroactive_not_in_extension_inheritance_clause,none,
2458+
"'retroactive' attribute only applies in inheritance clauses in "
2459+
"extensions", ())
2460+
2461+
ERROR(retroactive_attr_does_not_apply,none,
2462+
"'retroactive' attribute does not apply; %0 is declared in this module",
2463+
(Identifier))
2464+
2465+
WARNING(extension_retroactive_conformance,none,
2466+
"extension declares a conformance of imported type %0 to imported "
2467+
"%select{protocols|protocol}1 %2; this will not behave correctly if "
2468+
"the owners of %3 introduce this conformance in the future",
2469+
(Identifier, bool, StringRef, Identifier))
2470+
2471+
NOTE(extension_retroactive_conformance_silence,none,
2472+
"add '@retroactive' to silence this warning", ())
2473+
24572474
// Protocols
24582475
ERROR(type_does_not_conform,none,
24592476
"type %0 does not conform to protocol %1", (Type, Type))

include/swift/AST/Module.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,13 @@ class ModuleDecl
540540
/// module if one exists.
541541
ModuleDecl *getUnderlyingModuleIfOverlay() const;
542542

543+
/// Returns true if this module is the Clang overlay of \p other.
544+
bool isClangOverlayOf(ModuleDecl *other);
545+
546+
/// Returns true if this module is the same module or either module is a clang
547+
/// overlay of the other.
548+
bool isSameModuleLookingThroughOverlays(ModuleDecl *other);
549+
543550
/// Returns true if this module is an underscored cross import overlay
544551
/// declared by \p other or its underlying clang module, either directly or
545552
/// transitively (via intermediate cross-import overlays - for cross-imports

include/swift/AST/ProtocolConformance.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,11 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
306306
/// is either the default definition or was otherwise deduced.
307307
bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const;
308308

309+
/// Determines whether this conformance is retroactive; that is, if the
310+
/// conformance's declaration is in a different module from both the
311+
/// conforming type and the protocol.
312+
bool isRetroactive() const;
313+
309314
/// Print a parseable and human-readable description of the identifying
310315
/// information of the protocol conformance.
311316
void printName(raw_ostream &os,

include/swift/AST/TypeRepr.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr
147147
SourceLoc getEndLoc() const;
148148
SourceRange getSourceRange() const;
149149

150-
/// Find an @unchecked attribute and return its source location, or return
151-
/// an invalid source location if there is no such attribute.
152-
SourceLoc findUncheckedAttrLoc() const;
150+
/// Find an attribute with the provided kind and return its source location,
151+
/// or return an invalid source location if there is no such attribute.
152+
SourceLoc findAttrLoc(TypeAttrKind kind) const;
153153

154154
/// Is this type grammatically a type-simple?
155155
inline bool isSimple() const; // bottom of this file

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ LANGUAGE_FEATURE(MoveOnlyResilientTypes, 390, "non-@frozen noncopyable types wit
111111
LANGUAGE_FEATURE(ParameterPacks, 393, "Value and type parameter packs", true)
112112
SUPPRESSIBLE_LANGUAGE_FEATURE(LexicalLifetimes, 0, "@_eagerMove/@_noEagerMove/@_lexicalLifetimes annotations", true)
113113
LANGUAGE_FEATURE(FreestandingMacros, 397, "freestanding declaration macros", true)
114+
SUPPRESSIBLE_LANGUAGE_FEATURE(RetroactiveAttribute, 364, "@retroactive", true)
114115

115116
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
116117
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)

lib/AST/ASTPrinter.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2579,6 +2579,9 @@ void PrintAST::printInherited(const Decl *decl) {
25792579
interleave(TypesToPrint, [&](InheritedEntry inherited) {
25802580
if (inherited.isUnchecked)
25812581
Printer << "@unchecked ";
2582+
if (inherited.isRetroactive &&
2583+
!llvm::is_contained(Options.ExcludeAttrList, TAK_retroactive))
2584+
Printer << "@retroactive ";
25822585

25832586
printTypeLoc(inherited);
25842587
}, [&]() {
@@ -3049,6 +3052,18 @@ static bool usesFeatureGlobalActors(Decl *decl) {
30493052
return false;
30503053
}
30513054

3055+
static bool usesFeatureRetroactiveAttribute(Decl *decl) {
3056+
auto ext = dyn_cast<ExtensionDecl>(decl);
3057+
if (!ext)
3058+
return false;
3059+
3060+
ArrayRef<InheritedEntry> entries = ext->getInherited().getEntries();
3061+
return std::find_if(entries.begin(), entries.end(),
3062+
[](const InheritedEntry &entry) {
3063+
return entry.isRetroactive;
3064+
}) != entries.end();
3065+
}
3066+
30523067
static bool usesBuiltinType(Decl *decl, BuiltinTypeKind kind) {
30533068
auto typeMatches = [kind](Type type) {
30543069
return type.findIf([&](Type type) {
@@ -3477,6 +3492,14 @@ suppressingFeatureNoAsyncAvailability(PrintOptions &options,
34773492
action();
34783493
}
34793494

3495+
static void suppressingFeatureRetroactiveAttribute(
3496+
PrintOptions &options,
3497+
llvm::function_ref<void()> action) {
3498+
llvm::SaveAndRestore<PrintOptions> originalOptions(options);
3499+
options.ExcludeAttrList.push_back(TAK_retroactive);
3500+
action();
3501+
}
3502+
34803503
static bool usesFeatureReferenceBindings(Decl *decl) {
34813504
auto *vd = dyn_cast<VarDecl>(decl);
34823505
return vd && vd->getIntroducer() == VarDecl::Introducer::InOut;
@@ -7894,7 +7917,8 @@ swift::getInheritedForPrinting(
78947917
}
78957918

78967919
Results.push_back({TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()),
7897-
isUnchecked});
7920+
isUnchecked,
7921+
/*isRetroactive=*/false});
78987922
}
78997923
}
79007924

lib/AST/Decl.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,8 +1558,10 @@ NominalTypeDecl::takeConformanceLoaderSlow() {
15581558

15591559
InheritedEntry::InheritedEntry(const TypeLoc &typeLoc)
15601560
: TypeLoc(typeLoc), isUnchecked(false) {
1561-
if (auto typeRepr = typeLoc.getTypeRepr())
1562-
isUnchecked = typeRepr->findUncheckedAttrLoc().isValid();
1561+
if (auto typeRepr = typeLoc.getTypeRepr()) {
1562+
isUnchecked = typeRepr->findAttrLoc(TAK_unchecked).isValid();
1563+
isRetroactive = typeRepr->findAttrLoc(TAK_retroactive).isValid();
1564+
}
15631565
}
15641566

15651567
InheritedTypes::InheritedTypes(

lib/AST/Module.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2927,6 +2927,23 @@ ModuleDecl::getDeclaringModuleAndBystander() {
29272927
return *(declaringModuleAndBystander = {nullptr, Identifier()});
29282928
}
29292929

2930+
bool ModuleDecl::isClangOverlayOf(ModuleDecl *potentialUnderlying) {
2931+
return getUnderlyingModuleIfOverlay() == potentialUnderlying;
2932+
}
2933+
2934+
bool ModuleDecl::isSameModuleLookingThroughOverlays(
2935+
ModuleDecl *other) {
2936+
if (this == other) {
2937+
return true;
2938+
}
2939+
2940+
if (this->isClangOverlayOf(other) || other->isClangOverlayOf(this)) {
2941+
return true;
2942+
}
2943+
2944+
return false;
2945+
}
2946+
29302947
bool ModuleDecl::isCrossImportOverlayOf(ModuleDecl *other) {
29312948
ModuleDecl *current = this;
29322949
ModuleDecl *otherClang = other->getUnderlyingModuleIfOverlay();

lib/AST/NameLookup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3664,7 +3664,7 @@ void swift::getDirectlyInheritedNominalTypeDecls(
36643664
auto inheritedTypes = InheritedTypes(decl);
36653665
if (TypeRepr *typeRepr = inheritedTypes.getTypeRepr(i)) {
36663666
loc = typeRepr->getLoc();
3667-
uncheckedLoc = typeRepr->findUncheckedAttrLoc();
3667+
uncheckedLoc = typeRepr->findAttrLoc(TAK_unchecked);
36683668
}
36693669

36703670
// Form the result.

lib/AST/ProtocolConformance.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,26 @@ usesDefaultDefinition(AssociatedTypeDecl *requirement) const {
178178
CONFORMANCE_SUBCLASS_DISPATCH(usesDefaultDefinition, (requirement))
179179
}
180180

181+
bool ProtocolConformance::isRetroactive() const {
182+
auto extensionModule = getDeclContext()->getParentModule();
183+
auto protocolModule = getProtocol()->getParentModule();
184+
if (extensionModule->isSameModuleLookingThroughOverlays(protocolModule)) {
185+
return false;
186+
}
187+
188+
auto conformingTypeDecl =
189+
ConformingType->getNominalOrBoundGenericNominal();
190+
if (conformingTypeDecl) {
191+
auto conformingTypeModule = conformingTypeDecl->getParentModule();
192+
if (extensionModule->
193+
isSameModuleLookingThroughOverlays(conformingTypeModule)) {
194+
return false;
195+
}
196+
}
197+
198+
return true;
199+
}
200+
181201
GenericEnvironment *ProtocolConformance::getGenericEnvironment() const {
182202
switch (getKind()) {
183203
case ProtocolConformanceKind::Inherited:

lib/AST/TypeRepr.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,11 @@ TypeRepr *TypeRepr::getWithoutParens() const {
119119
return repr;
120120
}
121121

122-
SourceLoc TypeRepr::findUncheckedAttrLoc() const {
122+
SourceLoc TypeRepr::findAttrLoc(TypeAttrKind kind) const {
123123
auto typeRepr = this;
124124
while (auto attrTypeRepr = dyn_cast<AttributedTypeRepr>(typeRepr)) {
125-
if (attrTypeRepr->getAttrs().has(TAK_unchecked)) {
126-
return attrTypeRepr->getAttrs().getLoc(TAK_unchecked);
125+
if (attrTypeRepr->getAttrs().has(kind)) {
126+
return attrTypeRepr->getAttrs().getLoc(kind);
127127
}
128128

129129
typeRepr = attrTypeRepr->getTypeRepr();

lib/ASTGen/Sources/ASTGen/Types.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,10 @@ extension ASTGenVisitor {
247247
fallthrough
248248

249249
case .autoclosure, .escaping, .noescape, .noDerivative, .async,
250-
.sendable, .unchecked, ._local, ._noMetadata, .pack_owned,
251-
.pack_guaranteed, .pack_inout, .pack_out, .pseudogeneric,
252-
.yields, .yield_once, .yield_many, .thin, .thick, .count,
253-
.unimplementable:
250+
.sendable, .retroactive, .unchecked, ._local, ._noMetadata,
251+
.pack_owned, .pack_guaranteed, .pack_inout, .pack_out,
252+
.pseudogeneric, .yields, .yield_once, .yield_many, .thin, .thick,
253+
.count, .unimplementable:
254254
TypeAttributes_addSimpleAttr(typeAttributes, typeAttrKind, atLoc, attrLoc)
255255

256256
case .opened, .pack_element, .differentiable, .convention,

lib/ClangImporter/ImportDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7095,7 +7095,7 @@ void SwiftDeclConverter::importObjCProtocols(
70957095
inheritedTypes.push_back(
70967096
InheritedEntry(
70977097
TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()),
7098-
/*isUnchecked=*/false));
7098+
/*isUnchecked=*/false, /*isRetroactive=*/false));
70997099
}
71007100
}
71017101

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,8 @@ class InheritedProtocolCollector {
736736
// Create a synthesized ExtensionDecl for the conformance.
737737
ASTContext &ctx = M->getASTContext();
738738
auto inherits = ctx.AllocateCopy(llvm::makeArrayRef(InheritedEntry(
739-
TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), isUnchecked)));
739+
TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), isUnchecked,
740+
/*isRetroactive=*/false)));
740741
auto extension =
741742
ExtensionDecl::create(ctx, SourceLoc(), nullptr, inherits,
742743
nominal->getModuleScopeContext(), nullptr);

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5138,7 +5138,7 @@ ProtocolConformance *swift::deriveImplicitSendableConformance(
51385138
// FIXME: This is a hack--we should give conformances real availability.
51395139
auto inherits = ctx.AllocateCopy(makeArrayRef(
51405140
InheritedEntry(TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()),
5141-
/*isUnchecked*/true)));
5141+
/*isUnchecked*/true, /*isRetroactive=*/false)));
51425142
// If you change the use of AtLoc in the ExtensionDecl, make sure you
51435143
// update isNonSendableExtension() in ASTPrinter.
51445144
auto extension = ExtensionDecl::create(ctx, attrMakingUnavailable->AtLoc,

0 commit comments

Comments
 (0)