diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 46b7756f2cf7f..334c6b5f0f576 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -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) diff --git a/include/swift/AST/CASTBridging.h b/include/swift/AST/CASTBridging.h index 23feb799537fe..2ebcfd572e4b0 100644 --- a/include/swift/AST/CASTBridging.h +++ b/include/swift/AST/CASTBridging.h @@ -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, diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 5f5545edbe204..7b9f5443b4469 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -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 diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 602c1d1b9aee0..b613bdf6b8461 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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)) diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index c5f24ad0f6eec..17de1ac41f676 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -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 diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 87841f93aaf81..c3b08b45bb394 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -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, diff --git a/include/swift/AST/TypeRepr.h b/include/swift/AST/TypeRepr.h index aec0dc9ff26f0..fbf24c80eea19 100644 --- a/include/swift/AST/TypeRepr.h +++ b/include/swift/AST/TypeRepr.h @@ -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 diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 1773a6974a9cb..464c985861126 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -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) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 4055858fa5f17..a97363f4d61e4 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -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); }, [&]() { @@ -3049,6 +3052,18 @@ static bool usesFeatureGlobalActors(Decl *decl) { return false; } +static bool usesFeatureRetroactiveAttribute(Decl *decl) { + auto ext = dyn_cast(decl); + if (!ext) + return false; + + ArrayRef 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) { @@ -3475,6 +3490,14 @@ suppressingFeatureNoAsyncAvailability(PrintOptions &options, action(); } +static void suppressingFeatureRetroactiveAttribute( + PrintOptions &options, + llvm::function_ref action) { + llvm::SaveAndRestore originalOptions(options); + options.ExcludeAttrList.push_back(TAK_retroactive); + action(); +} + static bool usesFeatureReferenceBindings(Decl *decl) { auto *vd = dyn_cast(decl); return vd && vd->getIntroducer() == VarDecl::Introducer::InOut; @@ -7871,7 +7894,8 @@ swift::getInheritedForPrinting( } Results.push_back({TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), - isUnchecked}); + isUnchecked, + /*isRetroactive=*/false}); } } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 4a89214c7172c..2ca07c5acf5d1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -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( diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 9d240a190b2bb..30f088090329f 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -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(); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 65958444696d9..d5ffd744679e8 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -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. diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index db19b39b5552a..28120aeb7f39f 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -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: diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 6a971d9ec0e13..07a6b90bfc199 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -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(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(); diff --git a/lib/ASTGen/Sources/ASTGen/Types.swift b/lib/ASTGen/Sources/ASTGen/Types.swift index 2523a2c612c92..4de4ee09525ad 100644 --- a/lib/ASTGen/Sources/ASTGen/Types.swift +++ b/lib/ASTGen/Sources/ASTGen/Types.swift @@ -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, diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index d3d7216e72c18..c751621e2fb3a 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -7094,7 +7094,7 @@ void SwiftDeclConverter::importObjCProtocols( inheritedTypes.push_back( InheritedEntry( TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), - /*isUnchecked=*/false)); + /*isUnchecked=*/false, /*isRetroactive=*/false)); } } diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 13d2c1c95297b..81b25c83569a8 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -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); diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 630b58efc68e0..f4a7314ac62b5 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -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, diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index bbc63c7075c15..612484e01bf70 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -57,6 +57,7 @@ #include "swift/Parse/Parser.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Strings.h" +#include "clang/Basic/Module.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/APSInt.h" @@ -1569,6 +1570,206 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { diagnoseClassWithoutInitializers(classDecl); } +/// Determines if a given TypeLoc is module qualified by checking if it's +/// of the form `.`. +static bool isModuleQualified(TypeRepr *repr, ModuleDecl *module) { + auto memberTy = dyn_cast(repr); + if (!memberTy) { + return false; + } + + // FIXME(ModQual): This needs to be updated once we have an explicit + // module qualification syntax. + IdentTypeRepr *baseIdent = + dyn_cast(memberTy->getBaseComponent()); + if (!baseIdent) { + return false; + } + return baseIdent->getNameRef().isSimpleName(module->getName()); +} + +/// If the provided type is an AttributedTypeRepr, unwraps it and provides both +/// the casted AttributedTypeRepr and the underlying type. +/// If the provided type is _not_ an AttributedTypeRepr, returns it unmodified +/// and returns `nullptr` for the attributed type. +static TypeRepr *unwrapAttributedRepr(TypeRepr *repr) { + if (auto attr = dyn_cast(repr)) { + return attr->getTypeRepr(); + } + return repr; +} + +/// Determines if this extension declares a conformance of a type declared +/// outside this module to a protocol declared outside this module (but only +/// in library evolution mode) +/// +/// Since there can only be one conformance of a type to a given protocol, +/// it's not supported to declare these conformances because any other framework +/// (including the source framework) can declare this conformance, which would +/// result in duplicate symbols at runtime. +static void diagnoseRetroactiveConformances( + ExtensionDecl *ext, DiagnosticEngine &diags) { + ModuleDecl *module = ext->getParentModule(); + + // Disable this for the Foundation module because of the interplay with + // _ObjectiveCBridgeable. + if (module->isFoundationModule()) { + return; + } + + // Don't warn for this if we see it in module interfaces. + if (ext->getParentSourceFile()->Kind == SourceFileKind::Interface) { + return; + } + + Type extendedType = ext->getExtendedType(); + NominalTypeDecl *extendedNominalDecl = ext->getExtendedNominal(); + if (!extendedNominalDecl) { + return; + } + + ModuleDecl *extTypeModule = extendedNominalDecl->getParentModule(); + + // If the type comes from the __ObjC clang header module, don't warn. + if (extTypeModule->getName().is(CLANG_HEADER_MODULE_NAME)) { + return; + } + + // At this point, we know we're extending a type declared outside this module. + // We better only be conforming it to protocols declared within this module. + llvm::SmallSetVector externalProtocols; + for (const InheritedEntry &entry : ext->getInherited().getEntries()) { + if (entry.getType().isNull()) { + continue; + } + + auto proto = + dyn_cast_or_null(entry.getType()->getAnyNominal()); + if (!proto) { + continue; + } + + // As a fallback, to support previous language versions, also allow + // this through if the protocol has been explicitly module-qualified. + TypeRepr *repr = unwrapAttributedRepr(entry.getTypeRepr()); + if (isModuleQualified(repr, proto->getParentModule())) { + continue; + } + + proto->walkInheritedProtocols([&](ProtocolDecl *decl) { + + // Get the original conformance of the extended type to this protocol. + auto conformanceRef = TypeChecker::conformsToProtocol( + extendedType, decl, ext->getParentModule()); + if (!conformanceRef.isConcrete()) { + + return TypeWalker::Action::Continue; + } + auto conformance = conformanceRef.getConcrete(); + + // If that conformance came from this extension, then we warn. Otherwise + // we will have diagnosed it on the extension that actually declares this + // specific conformance. + if (conformance->getDeclContext() != ext) { + return TypeWalker::Action::Continue; + } + + // If this isn't a retroactive conformance, skip it. + if (!conformance->isRetroactive()) { + // However, if this is the protocol in the inherited type entry, + // check to make sure it's not erroneously marked @retroactive when it's + // not actually retroactive. + if (decl == proto && entry.isRetroactive) { + auto loc = entry.getTypeRepr()->findAttrLoc(TAK_retroactive); + bool typeIsSameModule = + extTypeModule->isSameModuleLookingThroughOverlays(module); + auto incorrectTypeName = typeIsSameModule ? + extendedNominalDecl->getName() : proto->getName(); + diags.diagnose(loc, diag::retroactive_attr_does_not_apply, + incorrectTypeName) + .fixItRemove(SourceRange(loc, loc.getAdvancedLoc(1))); + return TypeWalker::Action::Stop; + } + return TypeWalker::Action::Continue; + } + + // If it's marked @retroactive, no need to warn. + if (entry.isRetroactive) { + return TypeWalker::Action::Continue; + } + + // If we've come this far, we know this extension is the first declaration + // of the conformance of the extended type to this protocol. + externalProtocols.insert(decl); + + return TypeWalker::Action::Continue; + }); + } + + // If we didn't find any violations, we're done. + if (externalProtocols.empty()) { + return; + } + + // Diagnose the list of protocols we're introducing a conformance to. + + llvm::SmallString<32> protocolList; + { + llvm::raw_svector_ostream os(protocolList); + llvm::interleaveComma(externalProtocols, os, [&os](ProtocolDecl *proto) { + os << "'" << proto->getName() << "'"; + }); + } + + ext->diagnose( + diag::extension_retroactive_conformance, + extendedNominalDecl->getName(), + externalProtocols.size() == 1, + protocolList.str(), + extTypeModule->getName()); + + // Now build up a set of fix-its to force the user to explicitly specify the + // declared conformances. + + auto diag = ext->diagnose(diag::extension_retroactive_conformance_silence); + + // First, find if we can insert `@retroactive ` within this extension + // declaration to silence the warning. Each one of these gets removed from the + // externalProtocols list, and that might end up being all of them. + for (const InheritedEntry &entry : ext->getInherited().getEntries()) { + auto protoDecl = + dyn_cast_or_null(entry.getType()->getAnyNominal()); + TypeRepr *repr = unwrapAttributedRepr(entry.getTypeRepr()); + if (protoDecl && externalProtocols.remove(protoDecl)) { + llvm::SmallString<32> qualifiedName; + llvm::raw_svector_ostream os(qualifiedName); + os << "@retroactive " << protoDecl->getName(); + diag.fixItReplace( + repr->getSourceRange(), qualifiedName.str()); + } + } + + // If we've hit every protocol declared by this extension, we're good to go. + if (externalProtocols.empty()) { + return; + } + + // Otherwise, we've got inherited protocols that are being implicitly declared + // by this extension. Create explicit declarations for these with + // '@retroactive' conformances. + + { + llvm::SmallString<32> additionalExtensions; + llvm::raw_svector_ostream os(additionalExtensions); + for (ProtocolDecl *proto : externalProtocols) { + os << "extension " << extendedNominalDecl->getName() << ": " + << "@retroactive " << proto->getName() + << " {}\n"; + } + diag.fixItInsert(ext->getStartLoc(), additionalExtensions.str()); + } +} + void TypeChecker::diagnoseDuplicateBoundVars(Pattern *pattern) { SmallVector boundVars; pattern->collectVariables(boundVars); @@ -3641,6 +3842,8 @@ class DeclChecker : public DeclVisitor { checkInheritanceClause(ED); + diagnoseRetroactiveConformances(ED, getASTContext().Diags); + // Only generic and protocol types are permitted to have // trailing where clauses. if (auto trailingWhereClause = ED->getTrailingWhereClause()) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 7782735541f0d..a24cd148433a8 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3196,6 +3196,24 @@ TypeResolver::resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr, attrs.clearAttribute(TAK_unchecked); } + if (attrs.has(TAK_retroactive)) { + ty = resolveType(repr, options); + if (!ty || ty->hasError()) return ty; + + SourceLoc loc = attrs.getLoc(TAK_retroactive); + + auto extension = dyn_cast_or_null(getDeclContext()); + bool isInInheritanceClause = options.is(TypeResolverContext::Inherited); + if (!isInInheritanceClause || !extension) { + diagnoseInvalid(repr, loc, + diag::retroactive_not_in_extension_inheritance_clause) + .fixItRemove(getTypeAttrRangeWithAt(getASTContext(), loc)); + ty = ErrorType::get(getASTContext()); + } + + attrs.clearAttribute(TAK_retroactive); + } + if (attrs.has(TAK_opened)) { ty = resolveOpenedExistentialArchetype(attrs, repr, options); } else if (attrs.has(TAK_pack_element)) { diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 40d18e78e43d0..40be4fa42274f 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3149,7 +3149,8 @@ class DeclDeserializer { continue; } inheritedTypes.push_back( - InheritedEntry(TypeLoc::withoutLoc(maybeType.get()), isUnchecked)); + InheritedEntry(TypeLoc::withoutLoc(maybeType.get()), isUnchecked, + /*isRetroactive=*/false)); } auto inherited = ctx.AllocateCopy(inheritedTypes); diff --git a/stdlib/public/Differentiation/SIMDDifferentiation.swift.gyb b/stdlib/public/Differentiation/SIMDDifferentiation.swift.gyb index 4c1e83f52111b..a1e10887ee17f 100644 --- a/stdlib/public/Differentiation/SIMDDifferentiation.swift.gyb +++ b/stdlib/public/Differentiation/SIMDDifferentiation.swift.gyb @@ -23,7 +23,7 @@ vectorscalarCounts = storagescalarCounts + [3] // Protocol conformances //===----------------------------------------------------------------------===// -extension SIMD${n}: AdditiveArithmetic where Scalar: FloatingPoint {} +extension SIMD${n}: @retroactive AdditiveArithmetic where Scalar: FloatingPoint {} extension SIMD${n}: Differentiable where diff --git a/test/Concurrency/sendable_preconcurrency.swift b/test/Concurrency/sendable_preconcurrency.swift index cc2825879eb46..3721d07e246cf 100644 --- a/test/Concurrency/sendable_preconcurrency.swift +++ b/test/Concurrency/sendable_preconcurrency.swift @@ -41,7 +41,7 @@ func testA(ns: NS, mt: MyType, mt2: MyType2, mt3: MyType3, sc: StrictClass, nsc: } } -extension NonStrictStruct: @unchecked Sendable { } +extension NonStrictStruct: @retroactive @unchecked Swift.Sendable { } class StrictSubclass: StrictClass { override func send(_ body: () -> ()) {} diff --git a/test/Concurrency/sendable_without_preconcurrency.swift b/test/Concurrency/sendable_without_preconcurrency.swift index b00658cee68f4..2a6d22454bf82 100644 --- a/test/Concurrency/sendable_without_preconcurrency.swift +++ b/test/Concurrency/sendable_without_preconcurrency.swift @@ -36,7 +36,7 @@ func testA(ns: NS, mt: MyType, mt2: MyType2, sc: StrictClass, nsc: NonStrictClas } } -extension NonStrictStruct: @unchecked Sendable { } +extension NonStrictStruct: @retroactive @unchecked Sendable { } class StrictSubclass: StrictClass { override func send(_ body: () -> ()) {} diff --git a/test/Concurrency/sendable_without_preconcurrency_2.swift b/test/Concurrency/sendable_without_preconcurrency_2.swift index 542aaaaf7df5e..215cad9a62c74 100644 --- a/test/Concurrency/sendable_without_preconcurrency_2.swift +++ b/test/Concurrency/sendable_without_preconcurrency_2.swift @@ -37,7 +37,7 @@ func testA(ns: NS, mt: MyType, mt2: MyType2, sc: StrictClass, nsc: NonStrictClas } } -extension NonStrictStruct: @unchecked Sendable { } +extension NonStrictStruct: @unchecked @retroactive Sendable { } class StrictSubclass: StrictClass { override func send(_ body: () -> ()) {} diff --git a/test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb b/test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb index 487d01ed4f2a9..044d9fdd6a730 100644 --- a/test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb +++ b/test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb @@ -44,9 +44,9 @@ extension Hashable { } } -extension CGSize: Hashable {} -extension CGPoint: Hashable {} -extension CGRect: Hashable {} +extension CGSize: @retroactive Hashable {} +extension CGPoint: @retroactive Hashable {} +extension CGRect: @retroactive Hashable {} % for ObjectType, ValueTypes in coercionTypes.items(): func bridgeNSNumberBackToSpecificType(object: ${ObjectType}, diff --git a/test/Constraints/bridging.swift b/test/Constraints/bridging.swift index 8453d9a8f51ae..faa6a9d95cc64 100644 --- a/test/Constraints/bridging.swift +++ b/test/Constraints/bridging.swift @@ -15,7 +15,7 @@ public class BridgedClassSub : BridgedClass { } // Attempt to bridge to a type from another module. We only allow this for a // few specific types, like String. -extension LazyFilterSequence.Iterator : _ObjectiveCBridgeable { // expected-error{{conformance of 'Iterator' to '_ObjectiveCBridgeable' can only be written in module 'Swift'}} +extension LazyFilterSequence.Iterator : @retroactive _ObjectiveCBridgeable { // expected-error{{conformance of 'Iterator' to '_ObjectiveCBridgeable' can only be written in module 'Swift'}} public typealias _ObjectiveCType = BridgedClassSub public func _bridgeToObjectiveC() -> _ObjectiveCType { diff --git a/test/Constraints/implicit_double_cgfloat_conversion.swift b/test/Constraints/implicit_double_cgfloat_conversion.swift index 653e136f4d4f9..32126db0864d0 100644 --- a/test/Constraints/implicit_double_cgfloat_conversion.swift +++ b/test/Constraints/implicit_double_cgfloat_conversion.swift @@ -217,7 +217,7 @@ func test_multi_argument_conversion_with_optional(d: Double, cgf: CGFloat) { test(cgf, d) // Ok (CGFloat -> Double and Double? -> CGFloat?) } -extension CGFloat: Hashable { +extension CGFloat: @retroactive Hashable { public func hash(into hasher: inout Hasher) { fatalError() } } diff --git a/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift b/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift index 278fb9d340760..f6071839a99a9 100644 --- a/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift +++ b/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift @@ -82,7 +82,7 @@ public func ~=(x: NSObject, y: NSObject) -> Bool { return true } -extension NSObject : Equatable, Hashable { +extension NSObject : @retroactive Equatable, @retroactive Hashable { public static func == (lhs: NSObject, rhs: NSObject) -> Bool { return lhs.isEqual(rhs) } diff --git a/test/Interop/Cxx/class/extensions-typechecker.swift b/test/Interop/Cxx/class/extensions-typechecker.swift index b9d5339e0193b..670ebaa8ae6d3 100644 --- a/test/Interop/Cxx/class/extensions-typechecker.swift +++ b/test/Interop/Cxx/class/extensions-typechecker.swift @@ -6,7 +6,7 @@ extension Outer.Space.Foo { func bar() -> Int32 { return a } } -extension Outer.Space.Foo: ExpressibleByIntegerLiteral { +extension Outer.Space.Foo: @retroactive ExpressibleByIntegerLiteral { public init(integerLiteral value: IntegerLiteralType) { self.init(a: Int32(value)) } diff --git a/test/Interop/Cxx/class/protocol-conformance-typechecker.swift b/test/Interop/Cxx/class/protocol-conformance-typechecker.swift index 29395fbcf7b2d..25d037f8687d4 100644 --- a/test/Interop/Cxx/class/protocol-conformance-typechecker.swift +++ b/test/Interop/Cxx/class/protocol-conformance-typechecker.swift @@ -34,7 +34,7 @@ protocol Invertible { extension HasOperatorExclaim: Invertible {} -extension HasOperatorEqualEqual: Equatable {} +extension HasOperatorEqualEqual: @retroactive Equatable {} protocol HasOperatorPlusEqualProtocol { diff --git a/test/ModuleInterface/retroactive-conformances.swift b/test/ModuleInterface/retroactive-conformances.swift new file mode 100644 index 0000000000000..015670b66e009 --- /dev/null +++ b/test/ModuleInterface/retroactive-conformances.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s +// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) +// RUN: %FileCheck %s < %t.swiftinterface + +// CHECK: #if compiler(>=5.3) && $RetroactiveAttribute +// CHECK: extension Swift.Int : @retroactive Swift.Identifiable { +// CHECK: public var id: Swift.Int { +// CHECK: get +// CHECK: } +// CHECK: public typealias ID = Swift.Int +// CHECK: } +// CHECK: #else +// CHECK: extension Swift.Int : Swift.Identifiable { +// CHECK: public var id: Swift.Int { +// CHECK: get +// CHECK: } +// CHECK: public typealias ID = Swift.Int +// CHECK: } +// CHECK: #endif +extension Int: @retroactive Identifiable { + public var id: Int { self } +} diff --git a/test/Sema/Inputs/implementation-only-import-in-decls-helper.swift b/test/Sema/Inputs/implementation-only-import-in-decls-helper.swift index c3f4d61e817c9..701990dc6b281 100644 --- a/test/Sema/Inputs/implementation-only-import-in-decls-helper.swift +++ b/test/Sema/Inputs/implementation-only-import-in-decls-helper.swift @@ -1,12 +1,12 @@ import NormalLibrary -extension NormalStruct: NormalProto { +extension NormalStruct: @retroactive NormalProto { public typealias Assoc = Int } -extension GenericStruct: NormalProto { +extension GenericStruct: @retroactive NormalProto { public typealias Assoc = Int } -extension NormalClass: NormalProto { +extension NormalClass: @retroactive NormalProto { public typealias Assoc = Int } diff --git a/test/Sema/accessibility_package_in_extension.swift b/test/Sema/accessibility_package_in_extension.swift index 15f75d31c54ab..3acef863bad53 100644 --- a/test/Sema/accessibility_package_in_extension.swift +++ b/test/Sema/accessibility_package_in_extension.swift @@ -20,7 +20,7 @@ public class Decl { //--- Client.swift import Lib -extension Decl: Equatable { +extension Decl: @retroactive Equatable { public static func == (lhs: Decl, rhs: Decl) -> Bool { return false } diff --git a/test/Sema/conformance_availability_overlapping.swift b/test/Sema/conformance_availability_overlapping.swift index e3a69f2282cb9..28b09d9155a84 100644 --- a/test/Sema/conformance_availability_overlapping.swift +++ b/test/Sema/conformance_availability_overlapping.swift @@ -6,11 +6,11 @@ import conformance_availability_overlapping_other -extension HasUnavailableConformance : P {} +extension HasUnavailableConformance : @retroactive P {} -extension HasConditionallyAvailableConformance : P {} +extension HasConditionallyAvailableConformance : @retroactive P {} -extension HasAlwaysAvailableConformance : P {} +extension HasAlwaysAvailableConformance : @retroactive P {} // expected-warning@-1 {{conformance of 'HasAlwaysAvailableConformance' to protocol 'P' was already stated in the type's module 'conformance_availability_overlapping_other'}} struct G {} diff --git a/test/Sema/extension_retroactive_conformances.swift b/test/Sema/extension_retroactive_conformances.swift new file mode 100644 index 0000000000000..e2fcc14ce44b3 --- /dev/null +++ b/test/Sema/extension_retroactive_conformances.swift @@ -0,0 +1,84 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -swift-version 5 -DLIBRARY %s -emit-module -module-name Library -o %t/Library.swiftmodule +// RUN: %target-typecheck-verify-swift -swift-version 5 -I %t + +#if LIBRARY +// Define a couple protocols with no requirements that're easy to conform to +public protocol SampleProtocol1 {} +public protocol SampleProtocol2 {} + +public struct Sample1 {} +public struct Sample2 {} +public struct Sample3 {} +public struct Sample4 {} +public struct Sample5 {} +public struct Sample6 {} + +public struct SampleAlreadyConforms: SampleProtocol1 {} +#else + +import Library + +extension Sample1: SampleProtocol1 {} // expected-warning {{extension declares a conformance of imported type 'Sample1' to imported protocol 'SampleProtocol1'}} +// expected-note @-1 {{add '@retroactive' to silence this warning}} {{20-35=@retroactive SampleProtocol1}} + +protocol InheritsSampleProtocol: SampleProtocol1 {} +protocol NestedInheritsSampleProtocol: InheritsSampleProtocol {} + +protocol InheritsMultipleSampleProtocols: SampleProtocol1 {} +protocol NestedInheritsMultipleSampleProtocols: InheritsSampleProtocol, SampleProtocol2 {} + +extension Sample2: InheritsSampleProtocol {} // expected-warning {{extension declares a conformance of imported type 'Sample2' to imported protocol 'SampleProtocol1'}} +// expected-note @-1 {{add '@retroactive' to silence this warning}} {{1-1=extension Sample2: @retroactive SampleProtocol1 {\}\n}} + +extension SampleAlreadyConforms: InheritsSampleProtocol {} // ok, SampleAlreadyConforms already conforms in the source module + +extension Sample3: NestedInheritsSampleProtocol {} // expected-warning {{extension declares a conformance of imported type 'Sample3' to imported protocol 'SampleProtocol1'}} +// expected-note @-1 {{add '@retroactive' to silence this warning}} {{1-1=extension Sample3: @retroactive SampleProtocol1 {\}\n}} + +extension Sample4: NestedInheritsMultipleSampleProtocols {} // expected-warning {{extension declares a conformance of imported type 'Sample4' to imported protocols 'SampleProtocol2', 'SampleProtocol1'}} +// expected-note @-1 {{add '@retroactive' to silence this warning}} {{1-1=extension Sample4: @retroactive SampleProtocol2 {\}\nextension Sample4: @retroactive SampleProtocol1 {\}\n}} + +extension Sample5: @retroactive SampleProtocol2, @retroactive SampleProtocol1 {} + +// ok, explicit @retroactive in previous extension silences the warning +extension Sample5: NestedInheritsMultipleSampleProtocols {} + +// Check that looking through typealiases replaces the underlying type + +typealias MySample6 = Sample6 + +extension MySample6: SampleProtocol1 {} // expected-warning {{extension declares a conformance of imported type 'Sample6' to imported protocol 'SampleProtocol1'}} +// expected-note @-1 {{add '@retroactive' to silence this warning}} {{22-37=@retroactive SampleProtocol1}} + +// Ensure module-qualifying both types still silences the warning + +extension Library.Sample6: Library.SampleProtocol2 {} // ok, module-qualified. + +protocol ClientProtocol {} + +// ok, conforming a type from another module to a protocol within this module is totally fine +extension Sample1: ClientProtocol {} + +struct Sample7: @retroactive SampleProtocol1 {} // expected-error {{'retroactive' attribute only applies in inheritance clauses in extensions}} + +extension Sample7: @retroactive ClientProtocol {} // expected-error {{'retroactive' attribute does not apply; 'Sample7' is declared in this module}} + +extension Int: @retroactive ClientProtocol {} // expected-error {{'retroactive' attribute does not apply; 'ClientProtocol' is declared in this module}} + +func f(_ x: @retroactive Int) {} // expected-error {{'retroactive' attribute only applies in inheritance clauses in extensions}} + +var x: @retroactive Int { 0 } // expected-error {{'retroactive' attribute only applies in inheritance clauses in extensions}} + +#if os(macOS) + +@available(macOS 11, *) +@_originallyDefinedIn(module: "Library", macOS 14) +public struct OriginallyDefinedInLibrary {} + +@available(macOS 14, *) +extension OriginallyDefinedInLibrary: SampleProtocol1 {} // ok, @_originallyDefinedIn attribute makes this authoritative + +#endif + +#endif diff --git a/test/Sema/fixits-derived-conformances.swift b/test/Sema/fixits-derived-conformances.swift index 177fdf1c7abb4..553d2a3dca0ad 100644 --- a/test/Sema/fixits-derived-conformances.swift +++ b/test/Sema/fixits-derived-conformances.swift @@ -4,18 +4,18 @@ import Types -extension GenericEnum: Equatable { } +extension GenericEnum: @retroactive Equatable { } // expected-error@-1 {{extension outside of file declaring generic enum 'GenericEnum' prevents automatic synthesis of '==' for protocol 'Equatable'}} -// expected-note@-2 {{add stubs for conformance}}{{35-35=\n public static func == (lhs: GenericEnum, rhs: GenericEnum) -> Bool {\n <#code#>\n \}\n}} +// expected-note@-2 {{add stubs for conformance}}{{48-48=\n public static func == (lhs: GenericEnum, rhs: GenericEnum) -> Bool {\n <#code#>\n \}\n}} -extension Struct: Equatable { } +extension Struct: @retroactive Equatable { } // expected-error@-1 {{extension outside of file declaring struct 'Struct' prevents automatic synthesis of '==' for protocol 'Equatable'}} -// expected-note@-2 {{add stubs for conformance}}{{30-30=\n public static func == (lhs: Struct, rhs: Struct) -> Bool {\n <#code#>\n \}\n}} -extension GenericStruct: Equatable { } +// expected-note@-2 {{add stubs for conformance}}{{43-43=\n public static func == (lhs: Struct, rhs: Struct) -> Bool {\n <#code#>\n \}\n}} +extension GenericStruct: @retroactive Equatable { } // expected-error@-1 {{extension outside of file declaring generic struct 'GenericStruct' prevents automatic synthesis of '==' for protocol 'Equatable'}} -// expected-note@-2 {{add stubs for conformance}}{{37-37=\n public static func == (lhs: GenericStruct, rhs: GenericStruct) -> Bool {\n <#code#>\n \}\n}} +// expected-note@-2 {{add stubs for conformance}}{{50-50=\n public static func == (lhs: GenericStruct, rhs: GenericStruct) -> Bool {\n <#code#>\n \}\n}} -extension Enum: CaseIterable { } +extension Enum: @retroactive CaseIterable { } // expected-error@-1 {{extension outside of file declaring enum 'Enum' prevents automatic synthesis of 'allCases' for protocol 'CaseIterable'}} -// expected-note@-2 {{add stubs for conformance}}{{31-31=\n public static var allCases: [Enum]\n}} +// expected-note@-2 {{add stubs for conformance}}{{44-44=\n public static var allCases: [Enum]\n}} diff --git a/test/Sema/implementation-only-import-in-decls.swift b/test/Sema/implementation-only-import-in-decls.swift index dee5fa0eb6d17..9b8e5160fe2ec 100644 --- a/test/Sema/implementation-only-import-in-decls.swift +++ b/test/Sema/implementation-only-import-in-decls.swift @@ -120,7 +120,7 @@ extension Array where Element == BadStruct { subscript(okay _: Int) -> Int { 0 } // okay } -extension Int: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}} +extension Int: @retroactive BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}} struct TestExtensionConformanceOkay {} extension TestExtensionConformanceOkay: BadProto {} // okay diff --git a/test/Sema/spi-in-decls.swift b/test/Sema/spi-in-decls.swift index bad6d5facce01..a6a3a5f8f29c1 100644 --- a/test/Sema/spi-in-decls.swift +++ b/test/Sema/spi-in-decls.swift @@ -9,15 +9,15 @@ import NormalLibrary @_spi(X) -extension NormalStruct: NormalProto { +extension NormalStruct: @retroactive NormalProto { public typealias Assoc = Int } @_spi(X) -extension GenericStruct: NormalProto { +extension GenericStruct: @retroactive NormalProto { public typealias Assoc = Int } @_spi(X) -extension NormalClass: NormalProto { +extension NormalClass: @retroactive NormalProto { public typealias Assoc = Int } diff --git a/test/Sema/spi-inlinable-conformances.swift b/test/Sema/spi-inlinable-conformances.swift index e8e7f25f8160f..33af15a38d198 100644 --- a/test/Sema/spi-inlinable-conformances.swift +++ b/test/Sema/spi-inlinable-conformances.swift @@ -10,15 +10,15 @@ import NormalLibrary @_spi(X) -extension NormalStruct: NormalProto { +extension NormalStruct: @retroactive NormalProto { public typealias Assoc = Int } @_spi(X) -extension GenericStruct: NormalProto { +extension GenericStruct: @retroactive NormalProto { public typealias Assoc = Int } @_spi(X) -extension NormalClass: NormalProto { +extension NormalClass: @retroactive NormalProto { public typealias Assoc = Int } diff --git a/test/attr/attr_ibaction.swift b/test/attr/attr_ibaction.swift index 13e7aa8306367..97398b913ee57 100644 --- a/test/attr/attr_ibaction.swift +++ b/test/attr/attr_ibaction.swift @@ -77,7 +77,7 @@ protocol CP2 : class { } // Teach the compiler that String is @objc-friendly without importing // Foundation. -extension String: _ObjectiveCBridgeable { +extension String: @retroactive _ObjectiveCBridgeable { @_semantics("convertToObjectiveC") public func _bridgeToObjectiveC() -> AnyObject { fatalError() } public static func _forceBridgeFromObjectiveC(_ x: AnyObject, result: inout String?) { fatalError() } public static func _conditionallyBridgeFromObjectiveC(_ x: AnyObject, result: inout String?) -> Bool { fatalError() } diff --git a/test/decl/enum/bool_raw_value.swift b/test/decl/enum/bool_raw_value.swift index 88905f4f532ad..616a863bed497 100644 --- a/test/decl/enum/bool_raw_value.swift +++ b/test/decl/enum/bool_raw_value.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -extension Bool: ExpressibleByIntegerLiteral { +extension Bool: @retroactive ExpressibleByIntegerLiteral { public init(integerLiteral value: Int) { self = value != 0 } diff --git a/test/decl/enum/objc_bool_raw_value.swift b/test/decl/enum/objc_bool_raw_value.swift index 5245ceac3e7f3..4887e245b6e56 100644 --- a/test/decl/enum/objc_bool_raw_value.swift +++ b/test/decl/enum/objc_bool_raw_value.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift // REQUIRES: objc_interop -extension Bool: ExpressibleByIntegerLiteral { +extension Bool: @retroactive ExpressibleByIntegerLiteral { public init(integerLiteral value: Int) { self = value != 0 } diff --git a/test/decl/protocol/conforms/Inputs/placement_module_B.swift b/test/decl/protocol/conforms/Inputs/placement_module_B.swift index 1290df5ca83ec..9a36c170fea0f 100644 --- a/test/decl/protocol/conforms/Inputs/placement_module_B.swift +++ b/test/decl/protocol/conforms/Inputs/placement_module_B.swift @@ -3,6 +3,6 @@ import placement_module_A public class MMSub4 : MMSuper4 { } -extension MMSuper3 : MMP3a { } +extension MMSuper3 : @retroactive MMP3a { } extension MMSub4 : MMP3a { } diff --git a/test/decl/protocol/conforms/failure.swift b/test/decl/protocol/conforms/failure.swift index 53bb7a0cc1887..859f3b45d8fab 100644 --- a/test/decl/protocol/conforms/failure.swift +++ b/test/decl/protocol/conforms/failure.swift @@ -122,7 +122,7 @@ struct BadCase3 : PA { // expected-error {{type 'BadCase3' does not conform to p } // rdar://problem/32215763 -extension UInt32: ExpressibleByStringLiteral {} +extension UInt32: @retroactive ExpressibleByStringLiteral {} // expected-error@-1 {{type 'UInt32' does not conform to protocol 'ExpressibleByStringLiteral'}} // expected-error@-2 {{type 'UInt32' does not conform to protocol 'ExpressibleByExtendedGraphemeClusterLiteral'}} // expected-error@-3 {{type 'UInt32' does not conform to protocol 'ExpressibleByUnicodeScalarLiteral'}} diff --git a/test/decl/protocol/conforms/placement.swift b/test/decl/protocol/conforms/placement.swift index fed55c4ea333f..5bd183b75a5b1 100644 --- a/test/decl/protocol/conforms/placement.swift +++ b/test/decl/protocol/conforms/placement.swift @@ -192,26 +192,26 @@ extension MMExplicit1 : MMP1 { } // expected-warning{{conformance of 'MMExplicit extension MMExplicit1 : MMP2a { } // expected-warning{{MMExplicit1' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A}} extension MMExplicit1 : MMP3a { } // expected-warning{{conformance of 'MMExplicit1' to protocol 'MMP3a' was already stated in the type's module 'placement_module_A'}} -extension MMExplicit1 : MMP3b { } // okay +extension MMExplicit1 : @retroactive MMP3b { } // okay extension MMSuper1 : MMP1 { } // expected-warning{{conformance of 'MMSuper1' to protocol 'MMP1' was already stated in the type's module 'placement_module_A'}} extension MMSuper1 : MMP2a { } // expected-warning{{conformance of 'MMSuper1' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A'}} -extension MMSuper1 : MMP3b { } // okay +extension MMSuper1 : @retroactive MMP3b { } // okay extension MMSub1 : AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}} extension MMSub2 : MMP1 { } // expected-warning{{conformance of 'MMSub2' to protocol 'MMP1' was already stated in the type's module 'placement_module_A'}} extension MMSub2 : MMP2a { } // expected-warning{{conformance of 'MMSub2' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A'}} -extension MMSub2 : MMP3b { } // okay +extension MMSub2 : @retroactive MMP3b { } // okay -extension MMSub2 : MMAnyObjectRefinement { } // okay +extension MMSub2 : @retroactive MMAnyObjectRefinement { } // okay extension MMSub3 : MMP1 { } // expected-warning{{conformance of 'MMSub3' to protocol 'MMP1' was already stated in the type's module 'placement_module_A'}} extension MMSub3 : MMP2a { } // expected-warning{{conformance of 'MMSub3' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A'}} -extension MMSub3 : MMP3b { } // okay +extension MMSub3 : @retroactive MMP3b { } // okay extension MMSub3 : AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}} extension MMSub4 : MMP1 { } // expected-warning{{conformance of 'MMSub4' to protocol 'MMP1' was already stated in the type's module 'placement_module_B'}} extension MMSub4 : MMP2a { } // expected-warning{{conformance of 'MMSub4' to protocol 'MMP2a' was already stated in the type's module 'placement_module_B'}} -extension MMSub4 : MMP3b { } // okay +extension MMSub4 : @retroactive MMP3b { } // okay extension MMSub4 : AnyObjectRefinement { } // okay