Skip to content

@retroactive conformance syntax and checking #36068

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ TYPE_ATTR(differentiable)
TYPE_ATTR(noDerivative)
TYPE_ATTR(async)
TYPE_ATTR(Sendable)
TYPE_ATTR(retroactive)
TYPE_ATTR(unchecked)
TYPE_ATTR(_local)
TYPE_ATTR(_noMetadata)
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/CASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ typedef enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedTypeAttrKind : size_t {
BridgedTypeAttrKind_noDerivative,
BridgedTypeAttrKind_async,
BridgedTypeAttrKind_Sendable,
BridgedTypeAttrKind_retroactive,
BridgedTypeAttrKind_unchecked,
BridgedTypeAttrKind__local,
BridgedTypeAttrKind__noMetadata,
Expand Down
8 changes: 6 additions & 2 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1563,10 +1563,14 @@ struct InheritedEntry : public TypeLoc {
/// Whether there was an @unchecked attribute.
bool isUnchecked = false;

/// Whether there was an @retroactive attribute.
bool isRetroactive = false;

InheritedEntry(const TypeLoc &typeLoc);

InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked)
: TypeLoc(typeLoc), isUnchecked(isUnchecked) { }
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive)
: TypeLoc(typeLoc), isUnchecked(isUnchecked), isRetroactive(isRetroactive) {
}
};

/// A wrapper for the collection of inherited types for either a `TypeDecl` or
Expand Down
17 changes: 17 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2444,6 +2444,23 @@ NOTE(invalid_extension_rewrite,none,
ERROR(synthesized_nominal_extension,none,
"cannot extend synthesized type %0", (Type))

ERROR(retroactive_not_in_extension_inheritance_clause,none,
"'retroactive' attribute only applies in inheritance clauses in "
"extensions", ())

ERROR(retroactive_attr_does_not_apply,none,
"'retroactive' attribute does not apply; %0 is declared in this module",
(Identifier))

WARNING(extension_retroactive_conformance,none,
"extension declares a conformance of imported type %0 to imported "
"%select{protocols|protocol}1 %2; this will not behave correctly if "
"the owners of %3 introduce this conformance in the future",
(Identifier, bool, StringRef, Identifier))

NOTE(extension_retroactive_conformance_silence,none,
"add '@retroactive' to silence this warning", ())

// Protocols
ERROR(type_does_not_conform,none,
"type %0 does not conform to protocol %1", (Type, Type))
Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,13 @@ class ModuleDecl
/// module if one exists.
ModuleDecl *getUnderlyingModuleIfOverlay() const;

/// Returns true if this module is the Clang overlay of \p other.
bool isClangOverlayOf(ModuleDecl *other);

/// Returns true if this module is the same module or either module is a clang
/// overlay of the other.
bool isSameModuleLookingThroughOverlays(ModuleDecl *other);

/// Returns true if this module is an underscored cross import overlay
/// declared by \p other or its underlying clang module, either directly or
/// transitively (via intermediate cross-import overlays - for cross-imports
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/ProtocolConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
/// is either the default definition or was otherwise deduced.
bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const;

/// Determines whether this conformance is retroactive; that is, if the
/// conformance's declaration is in a different module from both the
/// conforming type and the protocol.
bool isRetroactive() const;

/// Print a parseable and human-readable description of the identifying
/// information of the protocol conformance.
void printName(raw_ostream &os,
Expand Down
6 changes: 3 additions & 3 deletions include/swift/AST/TypeRepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr
SourceLoc getEndLoc() const;
SourceRange getSourceRange() const;

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

/// Is this type grammatically a type-simple?
inline bool isSimple() const; // bottom of this file
Expand Down
1 change: 1 addition & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ LANGUAGE_FEATURE(MoveOnly, 390, "noncopyable types", true)
LANGUAGE_FEATURE(ParameterPacks, 393, "Value and type parameter packs", true)
SUPPRESSIBLE_LANGUAGE_FEATURE(LexicalLifetimes, 0, "@_eagerMove/@_noEagerMove/@_lexicalLifetimes annotations", true)
LANGUAGE_FEATURE(FreestandingMacros, 397, "freestanding declaration macros", true)
SUPPRESSIBLE_LANGUAGE_FEATURE(RetroactiveAttribute, 364, "@retroactive", true)

UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)
Expand Down
26 changes: 25 additions & 1 deletion lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2579,6 +2579,9 @@ void PrintAST::printInherited(const Decl *decl) {
interleave(TypesToPrint, [&](InheritedEntry inherited) {
if (inherited.isUnchecked)
Printer << "@unchecked ";
if (inherited.isRetroactive &&
!llvm::is_contained(Options.ExcludeAttrList, TAK_retroactive))
Printer << "@retroactive ";

printTypeLoc(inherited);
}, [&]() {
Expand Down Expand Up @@ -3049,6 +3052,18 @@ static bool usesFeatureGlobalActors(Decl *decl) {
return false;
}

static bool usesFeatureRetroactiveAttribute(Decl *decl) {
auto ext = dyn_cast<ExtensionDecl>(decl);
if (!ext)
return false;

ArrayRef<InheritedEntry> entries = ext->getInherited().getEntries();
return std::find_if(entries.begin(), entries.end(),
[](const InheritedEntry &entry) {
return entry.isRetroactive;
}) != entries.end();
}

static bool usesBuiltinType(Decl *decl, BuiltinTypeKind kind) {
auto typeMatches = [kind](Type type) {
return type.findIf([&](Type type) {
Expand Down Expand Up @@ -3475,6 +3490,14 @@ suppressingFeatureNoAsyncAvailability(PrintOptions &options,
action();
}

static void suppressingFeatureRetroactiveAttribute(
PrintOptions &options,
llvm::function_ref<void()> action) {
llvm::SaveAndRestore<PrintOptions> originalOptions(options);
options.ExcludeAttrList.push_back(TAK_retroactive);
action();
}

static bool usesFeatureReferenceBindings(Decl *decl) {
auto *vd = dyn_cast<VarDecl>(decl);
return vd && vd->getIntroducer() == VarDecl::Introducer::InOut;
Expand Down Expand Up @@ -7871,7 +7894,8 @@ swift::getInheritedForPrinting(
}

Results.push_back({TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()),
isUnchecked});
isUnchecked,
/*isRetroactive=*/false});
}
}

Expand Down
6 changes: 4 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1544,8 +1544,10 @@ NominalTypeDecl::takeConformanceLoaderSlow() {

InheritedEntry::InheritedEntry(const TypeLoc &typeLoc)
: TypeLoc(typeLoc), isUnchecked(false) {
if (auto typeRepr = typeLoc.getTypeRepr())
isUnchecked = typeRepr->findUncheckedAttrLoc().isValid();
if (auto typeRepr = typeLoc.getTypeRepr()) {
isUnchecked = typeRepr->findAttrLoc(TAK_unchecked).isValid();
isRetroactive = typeRepr->findAttrLoc(TAK_retroactive).isValid();
}
}

InheritedTypes::InheritedTypes(
Expand Down
17 changes: 17 additions & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2909,6 +2909,23 @@ ModuleDecl::getDeclaringModuleAndBystander() {
return *(declaringModuleAndBystander = {nullptr, Identifier()});
}

bool ModuleDecl::isClangOverlayOf(ModuleDecl *potentialUnderlying) {
return getUnderlyingModuleIfOverlay() == potentialUnderlying;
}

bool ModuleDecl::isSameModuleLookingThroughOverlays(
ModuleDecl *other) {
if (this == other) {
return true;
}

if (this->isClangOverlayOf(other) || other->isClangOverlayOf(this)) {
return true;
}

return false;
}

bool ModuleDecl::isCrossImportOverlayOf(ModuleDecl *other) {
ModuleDecl *current = this;
ModuleDecl *otherClang = other->getUnderlyingModuleIfOverlay();
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3664,7 +3664,7 @@ void swift::getDirectlyInheritedNominalTypeDecls(
auto inheritedTypes = InheritedTypes(decl);
if (TypeRepr *typeRepr = inheritedTypes.getTypeRepr(i)) {
loc = typeRepr->getLoc();
uncheckedLoc = typeRepr->findUncheckedAttrLoc();
uncheckedLoc = typeRepr->findAttrLoc(TAK_unchecked);
}

// Form the result.
Expand Down
20 changes: 20 additions & 0 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,26 @@ usesDefaultDefinition(AssociatedTypeDecl *requirement) const {
CONFORMANCE_SUBCLASS_DISPATCH(usesDefaultDefinition, (requirement))
}

bool ProtocolConformance::isRetroactive() const {
auto extensionModule = getDeclContext()->getParentModule();
auto protocolModule = getProtocol()->getParentModule();
if (extensionModule->isSameModuleLookingThroughOverlays(protocolModule)) {
return false;
}

auto conformingTypeDecl =
ConformingType->getNominalOrBoundGenericNominal();
if (conformingTypeDecl) {
auto conformingTypeModule = conformingTypeDecl->getParentModule();
if (extensionModule->
isSameModuleLookingThroughOverlays(conformingTypeModule)) {
return false;
}
}

return true;
}

GenericEnvironment *ProtocolConformance::getGenericEnvironment() const {
switch (getKind()) {
case ProtocolConformanceKind::Inherited:
Expand Down
6 changes: 3 additions & 3 deletions lib/AST/TypeRepr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,11 @@ TypeRepr *TypeRepr::getWithoutParens() const {
return repr;
}

SourceLoc TypeRepr::findUncheckedAttrLoc() const {
SourceLoc TypeRepr::findAttrLoc(TypeAttrKind kind) const {
auto typeRepr = this;
while (auto attrTypeRepr = dyn_cast<AttributedTypeRepr>(typeRepr)) {
if (attrTypeRepr->getAttrs().has(TAK_unchecked)) {
return attrTypeRepr->getAttrs().getLoc(TAK_unchecked);
if (attrTypeRepr->getAttrs().has(kind)) {
return attrTypeRepr->getAttrs().getLoc(kind);
}

typeRepr = attrTypeRepr->getTypeRepr();
Expand Down
8 changes: 4 additions & 4 deletions lib/ASTGen/Sources/ASTGen/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,10 @@ extension ASTGenVisitor {
fallthrough

case .autoclosure, .escaping, .noescape, .noDerivative, .async,
.sendable, .unchecked, ._local, ._noMetadata, .pack_owned,
.pack_guaranteed, .pack_inout, .pack_out, .pseudogeneric,
.yields, .yield_once, .yield_many, .thin, .thick, .count,
.unimplementable:
.sendable, .retroactive, .unchecked, ._local, ._noMetadata,
.pack_owned, .pack_guaranteed, .pack_inout, .pack_out,
.pseudogeneric, .yields, .yield_once, .yield_many, .thin, .thick,
.count, .unimplementable:
TypeAttributes_addSimpleAttr(typeAttributes, typeAttrKind, atLoc, attrLoc)

case .opened, .pack_element, .differentiable, .convention,
Expand Down
2 changes: 1 addition & 1 deletion lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7094,7 +7094,7 @@ void SwiftDeclConverter::importObjCProtocols(
inheritedTypes.push_back(
InheritedEntry(
TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()),
/*isUnchecked=*/false));
/*isUnchecked=*/false, /*isRetroactive=*/false));
}
}

Expand Down
3 changes: 2 additions & 1 deletion lib/Frontend/ModuleInterfaceSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,8 @@ class InheritedProtocolCollector {
// Create a synthesized ExtensionDecl for the conformance.
ASTContext &ctx = M->getASTContext();
auto inherits = ctx.AllocateCopy(llvm::makeArrayRef(InheritedEntry(
TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), isUnchecked)));
TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), isUnchecked,
/*isRetroactive=*/false)));
auto extension =
ExtensionDecl::create(ctx, SourceLoc(), nullptr, inherits,
nominal->getModuleScopeContext(), nullptr);
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5145,7 +5145,7 @@ ProtocolConformance *GetImplicitSendableRequest::evaluate(
// FIXME: This is a hack--we should give conformances real availability.
auto inherits = ctx.AllocateCopy(makeArrayRef(
InheritedEntry(TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()),
/*isUnchecked*/true)));
/*isUnchecked*/true, /*isRetroactive=*/false)));
// If you change the use of AtLoc in the ExtensionDecl, make sure you
// update isNonSendableExtension() in ASTPrinter.
auto extension = ExtensionDecl::create(ctx, attrMakingUnavailable->AtLoc,
Expand Down
Loading