diff --git a/include/swift/AST/AccessScopeChecker.h b/include/swift/AST/AccessScopeChecker.h index 608f489cca110..b26e3095ad933 100644 --- a/include/swift/AST/AccessScopeChecker.h +++ b/include/swift/AST/AccessScopeChecker.h @@ -54,13 +54,13 @@ class TypeDeclFinder : public TypeWalker { Action walkToTypePre(Type T) override; public: - virtual Action visitNominalType(const NominalType *ty) { + virtual Action visitNominalType(NominalType *ty) { return Action::Continue; } - virtual Action visitBoundGenericType(const BoundGenericType *ty) { + virtual Action visitBoundGenericType(BoundGenericType *ty) { return Action::Continue; } - virtual Action visitTypeAliasType(const TypeAliasType *ty) { + virtual Action visitTypeAliasType(TypeAliasType *ty) { return Action::Continue; } }; @@ -72,9 +72,9 @@ class SimpleTypeDeclFinder : public TypeDeclFinder { /// The function to call when a ComponentIdentTypeRepr is seen. llvm::function_ref Callback; - Action visitNominalType(const NominalType *ty) override; - Action visitBoundGenericType(const BoundGenericType *ty) override; - Action visitTypeAliasType(const TypeAliasType *ty) override; + Action visitNominalType(NominalType *ty) override; + Action visitBoundGenericType(BoundGenericType *ty) override; + Action visitTypeAliasType(TypeAliasType *ty) override; public: explicit SimpleTypeDeclFinder( diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index cdc22f3fbd824..a8c5c68167e12 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2391,6 +2391,14 @@ ERROR(decl_from_implementation_only_module,none, "cannot use %0 here; %1 has been imported as " "'@_implementationOnly'", (DeclName, Identifier)) +ERROR(conformance_from_implementation_only_module,none, + "cannot use conformance of %0 to %1 here; %2 has been imported as " + "'@_implementationOnly'", + (Type, DeclName, Identifier)) +ERROR(assoc_conformance_from_implementation_only_module,none, + "cannot use conformance of %0 to %1 in associated type %3 (inferred as " + "%4); %2 has been imported as '@_implementationOnly'", + (Type, DeclName, Identifier, Type, Type)) // Derived conformances ERROR(cannot_synthesize_init_in_extension_of_nonfinal,none, diff --git a/lib/AST/AccessScopeChecker.cpp b/lib/AST/AccessScopeChecker.cpp index 66df111402add..bfab254d6a6dd 100644 --- a/lib/AST/AccessScopeChecker.cpp +++ b/lib/AST/AccessScopeChecker.cpp @@ -71,17 +71,17 @@ TypeWalker::Action TypeDeclFinder::walkToTypePre(Type T) { } TypeWalker::Action -SimpleTypeDeclFinder::visitNominalType(const NominalType *ty) { +SimpleTypeDeclFinder::visitNominalType(NominalType *ty) { return Callback(ty->getDecl()); } TypeWalker::Action -SimpleTypeDeclFinder::visitBoundGenericType(const BoundGenericType *ty) { +SimpleTypeDeclFinder::visitBoundGenericType(BoundGenericType *ty) { return Callback(ty->getDecl()); } TypeWalker::Action -SimpleTypeDeclFinder::visitTypeAliasType(const TypeAliasType *ty) { +SimpleTypeDeclFinder::visitTypeAliasType(TypeAliasType *ty) { return Callback(ty->getDecl()); } diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 27c31b81bbfd0..bb626d0670bf7 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -15,10 +15,13 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" +#include "TypeCheckAvailability.h" +#include "swift/AST/AccessScopeChecker.h" #include "swift/AST/Attr.h" #include "swift/AST/Decl.h" -#include "swift/AST/Initializer.h" #include "swift/AST/DeclContext.h" +#include "swift/AST/Initializer.h" +#include "swift/AST/ProtocolConformance.h" using namespace swift; using FragileFunctionKind = TypeChecker::FragileFunctionKind; @@ -99,16 +102,13 @@ enum class DowngradeToWarning: bool { }; bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, - const ValueDecl *D, + ConcreteDeclRef declRef, const DeclContext *DC, FragileFunctionKind Kind, bool TreatUsableFromInlineAsPublic) { + const ValueDecl *D = declRef.getDecl(); // Do some important fast-path checks that apply to all cases. - // Local declarations are OK. - if (D->getDeclContext()->isLocalContext()) - return false; - // Type parameters are OK. if (isa(D)) return false; @@ -119,7 +119,7 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, return true; // Check whether the declaration comes from a publically-imported module. - if (diagnoseDeclRefExportability(loc, D, DC)) + if (diagnoseDeclRefExportability(loc, declRef, DC)) return true; return false; @@ -130,6 +130,10 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, const DeclContext *DC, FragileFunctionKind Kind, bool TreatUsableFromInlineAsPublic) { + // Local declarations are OK. + if (D->getDeclContext()->isLocalContext()) + return false; + // Public declarations are OK. if (D->getFormalAccessScope(/*useDC=*/nullptr, TreatUsableFromInlineAsPublic).isPublic()) @@ -203,8 +207,89 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, return (downgradeToWarning == DowngradeToWarning::No); } +static bool diagnoseDeclExportability(SourceLoc loc, const ValueDecl *D, + const SourceFile &userSF) { + auto definingModule = D->getModuleContext(); + if (!userSF.isImportedImplementationOnly(definingModule)) + return false; + + // TODO: different diagnostics + ASTContext &ctx = definingModule->getASTContext(); + ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_implementation_only, + D->getDescriptiveKind(), D->getFullName()); + return true; +} + +static bool +diagnoseGenericArgumentsExportability(SourceLoc loc, + const SubstitutionMap &subs, + const SourceFile &userSF) { + bool hadAnyIssues = false; + for (ProtocolConformanceRef conformance : subs.getConformances()) { + if (!conformance.isConcrete()) + continue; + const ProtocolConformance *concreteConf = conformance.getConcrete(); + + SubstitutionMap subConformanceSubs = + concreteConf->getSubstitutions(userSF.getParentModule()); + diagnoseGenericArgumentsExportability(loc, subConformanceSubs, userSF); + + const RootProtocolConformance *rootConf = + concreteConf->getRootConformance(); + ModuleDecl *M = rootConf->getDeclContext()->getParentModule(); + if (!userSF.isImportedImplementationOnly(M)) + continue; + + ASTContext &ctx = M->getASTContext(); + ctx.Diags.diagnose(loc, diag::conformance_from_implementation_only_module, + rootConf->getType(), + rootConf->getProtocol()->getFullName(), M->getName()); + hadAnyIssues = true; + } + return hadAnyIssues; +} + +void TypeChecker::diagnoseGenericTypeExportability(const TypeLoc &TL, + const DeclContext *DC) { + class GenericTypeFinder : public TypeDeclFinder { + using Callback = llvm::function_ref; + + const SourceFile &SF; + Callback callback; + public: + GenericTypeFinder(const SourceFile &SF, Callback callback) + : SF(SF), callback(callback) {} + + Action visitBoundGenericType(BoundGenericType *ty) override { + ModuleDecl *useModule = SF.getParentModule(); + SubstitutionMap subs = ty->getContextSubstitutionMap(useModule, + ty->getDecl()); + callback(subs); + return Action::Continue; + } + + Action visitTypeAliasType(TypeAliasType *ty) override { + callback(ty->getSubstitutionMap()); + return Action::Continue; + } + }; + + assert(TL.getType() && "type not validated yet"); + + const SourceFile *SF = DC->getParentSourceFile(); + if (!SF) + return; + + TL.getType().walk(GenericTypeFinder(*SF, [&](SubstitutionMap subs) { + // FIXME: It would be nice to highlight just the part of the type that's + // problematic, but unfortunately the TypeRepr doesn't have the + // information we need and the Type doesn't easily map back to it. + (void)diagnoseGenericArgumentsExportability(TL.getLoc(), subs, *SF); + })); +} + bool TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, - const ValueDecl *D, + ConcreteDeclRef declRef, const DeclContext *DC) { // We're only interested in diagnosing uses from source files. auto userSF = DC->getParentSourceFile(); @@ -219,22 +304,12 @@ bool TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, if (!userSF->hasImplementationOnlyImports()) return false; - auto userModule = userSF->getParentModule(); - auto definingModule = D->getModuleContext(); - - // Nothing to diagnose in the very common case of the same module. - if (userModule == definingModule) - return false; - - // Nothing to diagnose in the very common case that the module is - // imported for use in signatures. - if (!userSF->isImportedImplementationOnly(definingModule)) - return false; - - // TODO: different diagnostics - diagnose(loc, diag::inlinable_decl_ref_implementation_only, - D->getDescriptiveKind(), D->getFullName()); - - // TODO: notes explaining why - return true; + const ValueDecl *D = declRef.getDecl(); + if (diagnoseDeclExportability(loc, D, *userSF)) + return true; + if (diagnoseGenericArgumentsExportability(loc, declRef.getSubstitutions(), + *userSF)) { + return true; + } + return false; } diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 1c1ac6e5048db..552123d6fa217 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -22,6 +22,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeCheckRequests.h" using namespace swift; @@ -1402,13 +1403,17 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, class ImplementationOnlyImportChecker : public DeclVisitor { - using CheckImplementationOnlyCallback = + using CheckImplementationOnlyTypeCallback = llvm::function_ref; + using CheckImplementationOnlyConformanceCallback = + llvm::function_ref; TypeChecker &TC; - void checkTypeImpl(Type type, const TypeRepr *typeRepr, const SourceFile *SF, - CheckImplementationOnlyCallback diagnose) { + void checkTypeImpl( + Type type, const TypeRepr *typeRepr, const SourceFile &SF, + CheckImplementationOnlyTypeCallback diagnoseType, + CheckImplementationOnlyConformanceCallback diagnoseConformance) { // Don't bother checking errors. if (type && type->hasError()) return; @@ -1421,10 +1426,10 @@ class ImplementationOnlyImportChecker const_cast(typeRepr)->walk(TypeReprIdentFinder( [&](const ComponentIdentTypeRepr *component) { ModuleDecl *M = component->getBoundDecl()->getModuleContext(); - if (!SF->isImportedImplementationOnly(M)) + if (!SF.isImportedImplementationOnly(M)) return true; - diagnose(component->getBoundDecl(), component); + diagnoseType(component->getBoundDecl(), component); foundAnyIssues = true; // We still continue even in the diagnostic case to report multiple // violations. @@ -1432,32 +1437,93 @@ class ImplementationOnlyImportChecker })); } - // FIXME: Are there places where we can be sure the TypeRepr fully accounts - // for the type, and therefore we could skip this extra work? - if (!foundAnyIssues && type) { - type.walk(SimpleTypeDeclFinder([&](const TypeDecl *typeDecl) { + // Note that if we have a type, we can't skip checking it even if the + // TypeRepr is okay, because that's how we check what conformances are + // being used. + // + // We still don't want to do this if we found issues with the TypeRepr, + // though, because that would result in some issues being reported twice. + if (foundAnyIssues || !type) + return; + + class ProblematicTypeFinder : public TypeDeclFinder { + const SourceFile &SF; + CheckImplementationOnlyTypeCallback diagnoseType; + CheckImplementationOnlyConformanceCallback diagnoseConformance; + public: + ProblematicTypeFinder( + const SourceFile &SF, + CheckImplementationOnlyTypeCallback diagnoseType, + CheckImplementationOnlyConformanceCallback diagnoseConformance) + : SF(SF), diagnoseType(diagnoseType), + diagnoseConformance(diagnoseConformance) {} + + void visitTypeDecl(const TypeDecl *typeDecl) { ModuleDecl *M = typeDecl->getModuleContext(); - if (!SF->isImportedImplementationOnly(M)) - return TypeWalker::Action::Continue; + if (!SF.isImportedImplementationOnly(M)) + return; - diagnose(typeDecl, /*typeRepr*/nullptr); - // We still continue even in the diagnostic case to report multiple - // violations. - return TypeWalker::Action::Continue; - })); - } + diagnoseType(typeDecl, /*typeRepr*/nullptr); + } + + void visitSubstitutionMap(const SubstitutionMap &subs) { + for (ProtocolConformanceRef conformance : subs.getConformances()) { + if (!conformance.isConcrete()) + continue; + const ProtocolConformance *concreteConf = conformance.getConcrete(); + + SubstitutionMap subConformanceSubs = + concreteConf->getSubstitutions(SF.getParentModule()); + visitSubstitutionMap(subConformanceSubs); + + const RootProtocolConformance *rootConf = + concreteConf->getRootConformance(); + ModuleDecl *M = rootConf->getDeclContext()->getParentModule(); + if (!SF.isImportedImplementationOnly(M)) + continue; + diagnoseConformance(rootConf); + } + } + + Action visitNominalType(NominalType *ty) override { + visitTypeDecl(ty->getDecl()); + return Action::Continue; + } + + Action visitBoundGenericType(BoundGenericType *ty) override { + visitTypeDecl(ty->getDecl()); + SubstitutionMap subs = + ty->getContextSubstitutionMap(SF.getParentModule(), ty->getDecl()); + visitSubstitutionMap(subs); + return Action::Continue; + } + + Action visitTypeAliasType(TypeAliasType *ty) override { + visitTypeDecl(ty->getDecl()); + visitSubstitutionMap(ty->getSubstitutionMap()); + return Action::Continue; + } + }; + + type.walk(ProblematicTypeFinder(SF, diagnoseType, diagnoseConformance)); } - void checkType(Type type, const TypeRepr *typeRepr, const Decl *context, - CheckImplementationOnlyCallback diagnose) { + void checkType( + Type type, const TypeRepr *typeRepr, const Decl *context, + CheckImplementationOnlyTypeCallback diagnoseType, + CheckImplementationOnlyConformanceCallback diagnoseConformance) { auto *SF = context->getDeclContext()->getParentSourceFile(); assert(SF && "checking a non-source declaration?"); - return checkTypeImpl(type, typeRepr, SF, diagnose); + return checkTypeImpl(type, typeRepr, *SF, diagnoseType, + diagnoseConformance); } - void checkType(const TypeLoc &TL, const Decl *context, - CheckImplementationOnlyCallback diagnose) { - checkType(TL.getType(), TL.getTypeRepr(), context, diagnose); + void checkType( + const TypeLoc &TL, const Decl *context, + CheckImplementationOnlyTypeCallback diagnoseType, + CheckImplementationOnlyConformanceCallback diagnoseConformance) { + checkType(TL.getType(), TL.getTypeRepr(), context, diagnoseType, + diagnoseConformance); } void checkGenericParams(const GenericParamList *params, @@ -1470,14 +1536,15 @@ class ImplementationOnlyImportChecker continue; assert(param->getInherited().size() == 1); checkType(param->getInherited().front(), owner, - getDiagnoseCallback(owner)); + getDiagnoseCallback(owner), getDiagnoseCallback(owner)); } forAllRequirementTypes(WhereClauseOwner( owner->getInnermostDeclContext(), const_cast(params)), [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, owner, getDiagnoseCallback(owner)); + checkType(type, typeRepr, owner, getDiagnoseCallback(owner), + getDiagnoseCallback(owner)); }); } @@ -1494,11 +1561,24 @@ class ImplementationOnlyImportChecker offendingType->getFullName(), M->getName()); highlightOffendingType(TC, diag, complainRepr); } + + void operator()(const ProtocolConformance *offendingConformance) { + ModuleDecl *M = offendingConformance->getDeclContext()->getParentModule(); + TC.diagnose(D, diag::conformance_from_implementation_only_module, + offendingConformance->getType(), + offendingConformance->getProtocol()->getFullName(), + M->getName()); + } }; - static_assert(std::is_convertible::value, - "DiagnoseGenerically has wrong call signature"); + static_assert( + std::is_convertible::value, + "DiagnoseGenerically has wrong call signature"); + static_assert( + std::is_convertible::value, + "DiagnoseGenerically has wrong call signature for conformance diags"); DiagnoseGenerically getDiagnoseCallback(const Decl *D) { return DiagnoseGenerically(TC, D); @@ -1544,7 +1624,7 @@ class ImplementationOnlyImportChecker if (auto *extension = dyn_cast(D->getDeclContext())) { checkType(extension->getExtendedTypeLoc(), extension, - getDiagnoseCallback(extension)); + getDiagnoseCallback(extension), getDiagnoseCallback(extension)); checkConstrainedExtensionRequirements(extension); } } @@ -1593,7 +1673,7 @@ class ImplementationOnlyImportChecker return; checkType(theVar->getInterfaceType(), /*typeRepr*/nullptr, theVar, - getDiagnoseCallback(theVar)); + getDiagnoseCallback(theVar), getDiagnoseCallback(theVar)); } /// \see visitPatternBindingDecl @@ -1612,7 +1692,8 @@ class ImplementationOnlyImportChecker if (shouldSkipChecking(anyVar)) return; - checkType(TP->getTypeLoc(), anyVar, getDiagnoseCallback(anyVar)); + checkType(TP->getTypeLoc(), anyVar, getDiagnoseCallback(anyVar), + getDiagnoseCallback(anyVar)); } void visitPatternBindingDecl(PatternBindingDecl *PBD) { @@ -1635,21 +1716,24 @@ class ImplementationOnlyImportChecker void visitTypeAliasDecl(TypeAliasDecl *TAD) { checkGenericParams(TAD->getGenericParams(), TAD); - checkType(TAD->getUnderlyingTypeLoc(), TAD, getDiagnoseCallback(TAD)); + checkType(TAD->getUnderlyingTypeLoc(), TAD, getDiagnoseCallback(TAD), + getDiagnoseCallback(TAD)); } void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { llvm::for_each(assocType->getInherited(), [&](TypeLoc requirement) { - checkType(requirement, assocType, getDiagnoseCallback(assocType)); + checkType(requirement, assocType, getDiagnoseCallback(assocType), + getDiagnoseCallback(assocType)); }); checkType(assocType->getDefaultDefinitionLoc(), assocType, - getDiagnoseCallback(assocType)); + getDiagnoseCallback(assocType), getDiagnoseCallback(assocType)); if (assocType->getTrailingWhereClause()) { forAllRequirementTypes(assocType, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, assocType, getDiagnoseCallback(assocType)); + checkType(type, typeRepr, assocType, getDiagnoseCallback(assocType), + getDiagnoseCallback(assocType)); }); } } @@ -1659,19 +1743,22 @@ class ImplementationOnlyImportChecker llvm::for_each(nominal->getInherited(), [&](TypeLoc nextInherited) { - checkType(nextInherited, nominal, getDiagnoseCallback(nominal)); + checkType(nextInherited, nominal, getDiagnoseCallback(nominal), + getDiagnoseCallback(nominal)); }); } void visitProtocolDecl(ProtocolDecl *proto) { llvm::for_each(proto->getInherited(), [&](TypeLoc requirement) { - checkType(requirement, proto, getDiagnoseCallback(proto)); + checkType(requirement, proto, getDiagnoseCallback(proto), + getDiagnoseCallback(proto)); }); if (proto->getTrailingWhereClause()) { forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, proto, getDiagnoseCallback(proto)); + checkType(type, typeRepr, proto, getDiagnoseCallback(proto), + getDiagnoseCallback(proto)); }); } } @@ -1679,35 +1766,42 @@ class ImplementationOnlyImportChecker void visitSubscriptDecl(SubscriptDecl *SD) { checkGenericParams(SD->getGenericParams(), SD); - for (auto &P : *SD->getIndices()) - checkType(P->getTypeLoc(), SD, getDiagnoseCallback(SD)); - checkType(SD->getElementTypeLoc(), SD, getDiagnoseCallback(SD)); + for (auto &P : *SD->getIndices()) { + checkType(P->getTypeLoc(), SD, getDiagnoseCallback(SD), + getDiagnoseCallback(SD)); + } + checkType(SD->getElementTypeLoc(), SD, getDiagnoseCallback(SD), + getDiagnoseCallback(SD)); } void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { checkGenericParams(fn->getGenericParams(), fn); for (auto *P : *fn->getParameters()) - checkType(P->getTypeLoc(), fn, getDiagnoseCallback(fn)); + checkType(P->getTypeLoc(), fn, getDiagnoseCallback(fn), + getDiagnoseCallback(fn)); } void visitFuncDecl(FuncDecl *FD) { visitAbstractFunctionDecl(FD); - checkType(FD->getBodyResultTypeLoc(), FD, getDiagnoseCallback(FD)); + checkType(FD->getBodyResultTypeLoc(), FD, getDiagnoseCallback(FD), + getDiagnoseCallback(FD)); } void visitEnumElementDecl(EnumElementDecl *EED) { if (!EED->hasAssociatedValues()) return; for (auto &P : *EED->getParameterList()) - checkType(P->getTypeLoc(), EED, getDiagnoseCallback(EED)); + checkType(P->getTypeLoc(), EED, getDiagnoseCallback(EED), + getDiagnoseCallback(EED)); } void checkConstrainedExtensionRequirements(ExtensionDecl *ED) { if (!ED->getTrailingWhereClause()) return; forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ED, getDiagnoseCallback(ED)); + checkType(type, typeRepr, ED, getDiagnoseCallback(ED), + getDiagnoseCallback(ED)); }); } @@ -1719,7 +1813,8 @@ class ImplementationOnlyImportChecker // but just hide that from interfaces. llvm::for_each(ED->getInherited(), [&](TypeLoc nextInherited) { - checkType(nextInherited, ED, getDiagnoseCallback(ED)); + checkType(nextInherited, ED, getDiagnoseCallback(ED), + getDiagnoseCallback(ED)); }); if (!ED->getInherited().empty()) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 30ad38c6f4e01..8fa209ccc85ea 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2293,7 +2293,7 @@ class AvailabilityWalker : public ASTWalker { // DerivedConformanceRawRepresentable will do the right thing. flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailable; - diagAvailability(DR->getDecl(), DR->getSourceRange(), + diagAvailability(DR->getDeclRef(), DR->getSourceRange(), getEnclosingApplyExpr(), flags); maybeDiagStorageAccess(DR->getDecl(), DR->getSourceRange(), DC); } @@ -2302,18 +2302,18 @@ class AvailabilityWalker : public ASTWalker { return skipChildren(); } if (auto OCDR = dyn_cast(E)) - diagAvailability(OCDR->getDecl(), + diagAvailability(OCDR->getDeclRef(), OCDR->getConstructorLoc().getSourceRange(), getEnclosingApplyExpr()); if (auto DMR = dyn_cast(E)) - diagAvailability(DMR->getMember().getDecl(), + diagAvailability(DMR->getMember(), DMR->getNameLoc().getSourceRange(), getEnclosingApplyExpr()); if (auto DS = dyn_cast(E)) - diagAvailability(DS->getMember().getDecl(), DS->getSourceRange()); + diagAvailability(DS->getMember(), DS->getSourceRange()); if (auto S = dyn_cast(E)) { if (S->hasDecl()) { - diagAvailability(S->getDecl().getDecl(), S->getSourceRange()); + diagAvailability(S->getDecl(), S->getSourceRange()); maybeDiagStorageAccess(S->getDecl().getDecl(), S->getSourceRange(), DC); } } @@ -2328,7 +2328,7 @@ class AvailabilityWalker : public ASTWalker { walkInOutExpr(IO); return skipChildren(); } - + return visitChildren(); } @@ -2339,16 +2339,16 @@ class AvailabilityWalker : public ASTWalker { return E; } - bool diagAvailability(const ValueDecl *D, SourceRange R, + bool diagAvailability(ConcreteDeclRef declRef, SourceRange R, const ApplyExpr *call = nullptr, - DeclAvailabilityFlags flags = None); + DeclAvailabilityFlags flags = None) const; private: bool diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R, - const AvailableAttr *Attr); + const AvailableAttr *Attr) const; bool diagnoseMemoryLayoutMigration(const ValueDecl *D, SourceRange R, const AvailableAttr *Attr, - const ApplyExpr *call); + const ApplyExpr *call) const; /// Walks up from a potential callee to the enclosing ApplyExpr. const ApplyExpr *getEnclosingApplyExpr() const { @@ -2502,10 +2502,7 @@ class AvailabilityWalker : public ASTWalker { DeclAvailabilityFlags Flags) const { Flags &= DeclAvailabilityFlag::ForInout; Flags |= DeclAvailabilityFlag::ContinueOnPotentialUnavailability; - if (diagnoseDeclAvailability(D, TC, - const_cast(ReferenceDC), - ReferenceRange, - Flags)) + if (diagAvailability(D, ReferenceRange, /*call*/nullptr, Flags)) return; } }; @@ -2514,11 +2511,12 @@ class AvailabilityWalker : public ASTWalker { /// Diagnose uses of unavailable declarations. Returns true if a diagnostic /// was emitted. bool -AvailabilityWalker::diagAvailability(const ValueDecl *D, SourceRange R, +AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R, const ApplyExpr *call, - DeclAvailabilityFlags Flags) { - if (!D) + DeclAvailabilityFlags Flags) const { + if (!declRef) return false; + const ValueDecl *D = declRef.getDecl(); if (auto *attr = AvailableAttr::isUnavailable(D)) { if (diagnoseIncDecRemoval(D, R, attr)) @@ -2539,7 +2537,7 @@ AvailabilityWalker::diagAvailability(const ValueDecl *D, SourceRange R, if (FragileKind) if (R.isValid()) - if (TC.diagnoseInlinableDeclRef(R.Start, D, DC, *FragileKind, + if (TC.diagnoseInlinableDeclRef(R.Start, declRef, DC, *FragileKind, TreatUsableFromInlineAsPublic)) return true; @@ -2602,9 +2600,9 @@ static bool isIntegerOrFloatingPointType(Type ty, DeclContext *DC, /// If this is a call to an unavailable ++ / -- operator, try to diagnose it /// with a fixit hint and return true. If not, or if we fail, return false. -bool AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, - SourceRange R, - const AvailableAttr *Attr) { +bool +AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R, + const AvailableAttr *Attr) const { // We can only produce a fixit if we're talking about ++ or --. bool isInc = D->getBaseName() == "++"; if (!isInc && D->getBaseName() != "--") @@ -2664,10 +2662,11 @@ bool AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, /// If this is a call to an unavailable sizeof family function, diagnose it /// with a fixit hint and return true. If not, or if we fail, return false. -bool AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, - SourceRange R, - const AvailableAttr *Attr, - const ApplyExpr *call) { +bool +AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, + SourceRange R, + const AvailableAttr *Attr, + const ApplyExpr *call) const { if (!D->getModuleContext()->isStdlibModule()) return false; @@ -2750,5 +2749,5 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *Decl, DeclAvailabilityFlags Flags) { AvailabilityWalker AW(TC, DC); - return AW.diagAvailability(Decl, R, nullptr, Flags); + return AW.diagAvailability(const_cast(Decl), R, nullptr, Flags); } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 953931c248495..77f1e2f47a417 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -3643,23 +3643,70 @@ void ConformanceChecker::ensureRequirementsAreSatisfied( std::function writer = Conformance->populateSignatureConformances(); + SourceFile *fileForCheckingImplementationOnlyUse = nullptr; + if (getRequiredAccessScope().isPublic() || isUsableFromInlineRequired()) + fileForCheckingImplementationOnlyUse = DC->getParentSourceFile(); + class GatherConformancesListener : public GenericRequirementsCheckListener { - NormalProtocolConformance *conformance; + NormalProtocolConformance *conformanceBeingChecked; + SourceFile *SF; std::function &writer; + + void checkForImplementationOnlyUse(Type depTy, Type replacementTy, + const ProtocolConformance *conformance) { + if (!SF) + return; + + SubstitutionMap subs = + conformance->getSubstitutions(SF->getParentModule()); + for (auto &subConformance : subs.getConformances()) { + if (!subConformance.isConcrete()) + continue; + checkForImplementationOnlyUse(depTy, replacementTy, + subConformance.getConcrete()); + } + + const RootProtocolConformance *rootConformance = + conformance->getRootConformance(); + ModuleDecl *M = rootConformance->getDeclContext()->getParentModule(); + if (!SF->isImportedImplementationOnly(M)) + return; + + ASTContext &ctx = M->getASTContext(); + + Type selfTy = rootConformance->getProtocol()->getProtocolSelfType(); + if (depTy->isEqual(selfTy)) { + ctx.Diags.diagnose( + conformanceBeingChecked->getLoc(), + diag::conformance_from_implementation_only_module, + rootConformance->getType(), + rootConformance->getProtocol()->getName(), M->getName()); + } else { + ctx.Diags.diagnose( + conformanceBeingChecked->getLoc(), + diag::assoc_conformance_from_implementation_only_module, + rootConformance->getType(), + rootConformance->getProtocol()->getName(), M->getName(), + depTy, replacementTy->getCanonicalType()); + } + } + public: GatherConformancesListener( NormalProtocolConformance *conformance, - std::function &writer) - : conformance(conformance), writer(writer) { } + std::function &writer, + SourceFile *fileForCheckingImplementationOnlyUse) + : conformanceBeingChecked(conformance), + SF(fileForCheckingImplementationOnlyUse), writer(writer) { } void satisfiedConformance(Type depTy, Type replacementTy, ProtocolConformanceRef conformance) override { // The conformance will use contextual types, but we want the // interface type equivalent. - if (conformance.isConcrete() && - conformance.getConcrete()->getType()->hasArchetype()) { + if (conformance.isConcrete()) { auto concreteConformance = conformance.getConcrete(); + if (conformance.getConcrete()->getType()->hasArchetype()) { // Map the conformance. concreteConformance = concreteConformance->subst( [](SubstitutableType *type) -> Type { @@ -3669,7 +3716,12 @@ void ConformanceChecker::ensureRequirementsAreSatisfied( }, MakeAbstractConformanceForGenericType()); - conformance = ProtocolConformanceRef(concreteConformance); + + conformance = ProtocolConformanceRef(concreteConformance); + } + + checkForImplementationOnlyUse(depTy, replacementTy, + concreteConformance); } writer(conformance); @@ -3679,13 +3731,13 @@ void ConformanceChecker::ensureRequirementsAreSatisfied( const Requirement &req, Type first, Type second, ArrayRef parents) override { // Invalidate the conformance to suppress further diagnostics. - if (conformance->getLoc().isValid()) { - conformance->setInvalid(); + if (conformanceBeingChecked->getLoc().isValid()) { + conformanceBeingChecked->setInvalid(); } return false; } - } listener(Conformance, writer); + } listener(Conformance, writer, fileForCheckingImplementationOnlyUse); auto result = TC.checkGenericArguments( DC, Loc, Loc, diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 6d2443bb7b833..2fd04dca4d702 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1742,6 +1742,13 @@ bool TypeChecker::validateType(TypeLoc &Loc, TypeResolution resolution, } Loc.setType(type); + if (!type->hasError()) { + const DeclContext *DC = resolution.getDeclContext(); + if (options.isAnyExpr() || DC->getParent()->isLocalContext()) + if (DC->getResilienceExpansion() == ResilienceExpansion::Minimal) + diagnoseGenericTypeExportability(Loc, DC); + } + return type->hasError(); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index b76b4ebd0dacf..e8314d7a1d7f5 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1876,7 +1876,7 @@ class TypeChecker final : public LazyResolver { PropertyInitializer }; - bool diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, + bool diagnoseInlinableDeclRef(SourceLoc loc, ConcreteDeclRef declRef, const DeclContext *DC, FragileFunctionKind Kind, bool TreatUsableFromInlineAsPublic); @@ -1887,13 +1887,23 @@ class TypeChecker final : public LazyResolver { FragileFunctionKind Kind, bool TreatUsableFromInlineAsPublic); -public: /// Given that a declaration is used from a particular context which /// exposes it in the interface of the current module, diagnose if it cannot /// reasonably be shared. - bool diagnoseDeclRefExportability(SourceLoc loc, const ValueDecl *D, + bool diagnoseDeclRefExportability(SourceLoc loc, ConcreteDeclRef declRef, const DeclContext *DC); +public: + /// Given that a type is used from a particular context which + /// exposes it in the interface of the current module, diagnose if its + /// generic arguments require the use of conformances that cannot reasonably + /// be shared. + /// + /// This method \e only checks how generic arguments are used; it is assumed + /// that the declarations involved have already been checked elsewhere. + void diagnoseGenericTypeExportability(const TypeLoc &TL, + const DeclContext *DC); + /// Given that \p DC is within a fragile context for some reason, describe /// why. /// 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 4ef1dd4dfd9fe..8160a9bf833af 100644 --- a/test/Sema/Inputs/implementation-only-import-in-decls-helper.swift +++ b/test/Sema/Inputs/implementation-only-import-in-decls-helper.swift @@ -1,3 +1,15 @@ +import NormalLibrary + +extension NormalStruct: NormalProto { + public typealias Assoc = Int +} +extension GenericStruct: NormalProto { + public typealias Assoc = Int +} +extension NormalClass: NormalProto { + public typealias Assoc = Int +} + public struct BadStruct {} public protocol BadProto {} open class BadClass {} diff --git a/test/Sema/Inputs/implementation-only-import-in-decls-public-helper.swift b/test/Sema/Inputs/implementation-only-import-in-decls-public-helper.swift new file mode 100644 index 0000000000000..59a2f37c14620 --- /dev/null +++ b/test/Sema/Inputs/implementation-only-import-in-decls-public-helper.swift @@ -0,0 +1,6 @@ +public struct NormalStruct {} +public struct GenericStruct {} +public protocol NormalProto { + associatedtype Assoc +} +open class NormalClass {} diff --git a/test/Sema/implementation-only-import-in-decls.swift b/test/Sema/implementation-only-import-in-decls.swift index a24126cdef056..661453abc1f24 100644 --- a/test/Sema/implementation-only-import-in-decls.swift +++ b/test/Sema/implementation-only-import-in-decls.swift @@ -1,8 +1,10 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule %S/Inputs/implementation-only-import-in-decls-helper.swift +// RUN: %target-swift-frontend -emit-module -o %t/NormalLibrary.swiftmodule %S/Inputs/implementation-only-import-in-decls-public-helper.swift +// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule %S/Inputs/implementation-only-import-in-decls-helper.swift -I %t // RUN: %target-typecheck-verify-swift -I %t +import NormalLibrary @_implementationOnly import BADLibrary public struct TestConformance: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} @@ -62,6 +64,7 @@ public protocol TestAssocTypeWhereClause { public enum TestRawType: IntLike { // expected-error {{cannot use 'IntLike' here; 'BADLibrary' has been imported as '@_implementationOnly'}} case x = 1 + // FIXME: expected-error@-1 {{cannot use conformance of 'IntLike' to 'Equatable' here; 'BADLibrary' has been imported as '@_implementationOnly'}} } public class TestSubclass: BadClass { // expected-error {{cannot use 'BadClass' here; 'BADLibrary' has been imported as '@_implementationOnly'}} @@ -148,3 +151,108 @@ public class PublicClassStoredProperties { private static var staticIsOkay: BadStruct? // okay @usableFromInline internal var computedUFIIsNot: BadStruct? { return nil } // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} } + +public typealias NormalProtoAssoc = T.Assoc +public func testConformanceInTypealias(_: NormalProtoAssoc) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public struct NormalProtoAssocHolder { + public var value: T.Assoc +} +public func testConformanceInBoundGeneric(_: NormalProtoAssocHolder) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public class SubclassOfNormalClass: NormalClass {} + +public func testInheritedConformance(_: NormalProtoAssocHolder) {} // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public func testSpecializedConformance(_: NormalProtoAssocHolder>) {} // expected-error {{cannot use conformance of 'GenericStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +extension Array where Element == NormalProtoAssocHolder { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + public func testConstrainedExtensionUsingBadConformance() {} +} + +public struct ConditionalGenericStruct {} +extension ConditionalGenericStruct: NormalProto where T: NormalProto { + public typealias Assoc = Int +} +public func testConditionalGeneric(_: NormalProtoAssocHolder>) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public protocol PublicInferredAssociatedType { + associatedtype Assoc: NormalProto + func takesAssoc(_: Assoc) +} +@usableFromInline protocol UFIInferredAssociatedType { + associatedtype Assoc: NormalProto + func takesAssoc(_: Assoc) +} +protocol InternalInferredAssociatedType { + associatedtype Assoc: NormalProto + func takesAssoc(_: Assoc) +} + +public struct PublicInferredAssociatedTypeImpl { + public func takesAssoc(_: NormalStruct) {} +} +extension PublicInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} +extension PublicInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} +extension PublicInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay + +@usableFromInline struct UFIInferredAssociatedTypeImpl { + public func takesAssoc(_: NormalStruct) {} +} +extension UFIInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} +extension UFIInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} +extension UFIInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay + +struct InternalInferredAssociatedTypeImpl { + public func takesAssoc(_: NormalStruct) {} +} +extension InternalInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // okay +extension InternalInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // okay +extension InternalInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay + + +public protocol BaseProtoWithNoRequirement { + associatedtype Assoc + func takesAssoc(_: Assoc) +} +public protocol RefinedProto: BaseProtoWithNoRequirement where Assoc: NormalProto { +} + +public struct RefinedProtoImpl: RefinedProto { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: NormalStruct) {} +} + +public protocol RefinedSelfProto where Self: NormalProto {} +extension NormalStruct: RefinedSelfProto {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public protocol RefinedSelfProtoInheritance: NormalProto {} +extension NormalStruct: RefinedSelfProtoInheritance {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + +public protocol SlightlyMoreComplicatedRequirement { + associatedtype Assoc: Collection where Assoc.Element: NormalProto + func takesAssoc(_: Assoc) +} +public struct SlightlyMoreComplicatedRequirementImpl: SlightlyMoreComplicatedRequirement { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc.Element' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: [NormalStruct]) {} +} +public struct RequirementsHandleSubclassesToo: SlightlyMoreComplicatedRequirement { // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' in associated type 'Self.Assoc.Element' (inferred as 'SubclassOfNormalClass'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: [SubclassOfNormalClass]) {} +} + +public struct RequirementsHandleSpecializationsToo: SlightlyMoreComplicatedRequirement { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc.Element' (inferred as 'ConditionalGenericStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: [ConditionalGenericStruct]) {} +} + +public struct ClassConstrainedGenericArg: PublicInferredAssociatedType { // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'T'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: T) {} +} + + +public protocol RecursiveRequirements { + associatedtype Other: RecursiveRequirements +} +extension GenericStruct: RecursiveRequirements { + public typealias Other = GenericStruct +} +public struct RecursiveRequirementsHolder {} +public func makeSureRecursiveRequirementsDontBreakEverything(_: RecursiveRequirementsHolder>) {} diff --git a/test/Sema/implementation-only-import-inlinable-conformances.swift b/test/Sema/implementation-only-import-inlinable-conformances.swift new file mode 100644 index 0000000000000..99666ae88bfa1 --- /dev/null +++ b/test/Sema/implementation-only-import-inlinable-conformances.swift @@ -0,0 +1,263 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/NormalLibrary.swiftmodule %S/Inputs/implementation-only-import-in-decls-public-helper.swift +// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule %S/Inputs/implementation-only-import-in-decls-helper.swift -I %t + +// RUN: %target-typecheck-verify-swift -I %t + +@_implementationOnly import BADLibrary +import NormalLibrary + +@available(*, unavailable) +public typealias X = Int + +public typealias NormalProtoAssoc = T.Assoc +@inlinable func testConformanceInTypealias() { + let x: NormalProtoAssoc? = nil // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + _ = x + _ = NormalProtoAssoc() // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +func internalConformanceInTypealias() { + let x: NormalProtoAssoc? = nil // okay + _ = x + _ = NormalProtoAssoc() // okay +} + +public struct NormalProtoAssocHolder { + public var value: T.Assoc? + public init() {} + public init(_ value: T?) {} +} +@inlinable func testConformanceInBoundGeneric() { + let x: NormalProtoAssocHolder? = nil // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + _ = x + // FIXME: We get this error twice: once for the TypeExpr and once for the implicit init. + _ = NormalProtoAssocHolder() // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + _ = NormalProtoAssocHolder(nil as NormalStruct?) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +func internalConformanceInBoundGeneric() { + let x: NormalProtoAssocHolder? = nil // okay + _ = x + _ = NormalProtoAssocHolder() // okay + _ = NormalProtoAssocHolder(nil as NormalStruct?) // okay +} + +@inlinable func testDowncast(_ x: Any) -> Bool { + let normal = x is NormalProtoAssocHolder // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + let alias = x is NormalProtoAssoc // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + return normal || alias +} + +func internalDowncast(_ x: Any) -> Bool { + let normal = x is NormalProtoAssocHolder // okay + let alias = x is NormalProtoAssoc // okay + return normal || alias +} + +@inlinable func testSwitch(_ x: Any) { + switch x { + case let holder as NormalProtoAssocHolder: // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + _ = holder + break + case is NormalProtoAssoc: // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + break + default: + break + } +} + +func internalSwitch(_ x: Any) { + switch x { + case let holder as NormalProtoAssocHolder: // okay + _ = holder + break + case is NormalProtoAssoc: // okay + break + default: + break + } +} + +public enum NormalProtoEnumUser { + case a +} + +@inlinable func testEnum() { + // FIXME: We get this error twice: once for the pattern and once for the implicit TypeExpr. + let x: NormalProtoEnumUser = .a // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + _ = x + // FIXME: We get this error twice: once for the TypeExpr and once for the case. + _ = NormalProtoEnumUser.a // expected-error 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +func internalEnum() { + let x: NormalProtoEnumUser = .a // okay + _ = x + _ = NormalProtoEnumUser.a // okay +} + +@usableFromInline func testFuncImpl(_: T.Type) {} + +@inlinable func testFunc() { + testFuncImpl(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +func internalFunc() { + testFuncImpl(NormalStruct.self) // okay +} + +public struct ForTestingMembers { + public init() {} + public init(_: T.Type) {} + + public subscript(_: T.Type) -> Int { + get { return 0 } + set {} + } + + public func method(_: T.Type) {} +} + +@inlinable func testMembers() { + _ = ForTestingMembers(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + _ = ForTestingMembers.init(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + _ = ForTestingMembers()[NormalStruct.self] // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + var instance = ForTestingMembers() + instance[NormalStruct.self] = 1 // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + ForTestingMembers().method(NormalStruct.self) // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +extension NormalProtoAssocHolder { + public static func testAnotherConformance(_: U.Type) {} +} + +@inlinable func testMultipleConformances() { + _ = NormalProtoAssocHolder.testAnotherConformance(NormalClass.self) + // expected-error@-1 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + // expected-error@-2 {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +@inlinable func localTypeAlias() { + typealias LocalUser = NormalProtoAssocHolder // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + typealias LocalGenericUser = (T, NormalProtoAssocHolder) // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + typealias LocalProtoAssoc = T.Assoc + _ = LocalProtoAssoc() // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +@inlinable func localFunctions() { + func local(_: NormalProtoAssocHolder) {} // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + func localReturn() -> NormalProtoAssocHolder { fatalError() } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + let _ = { (_: NormalProtoAssocHolder) in return } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + let _ = { () -> NormalProtoAssocHolder in fatalError() } // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +@inlinable public func signatureOfInlinable(_: NormalProtoAssocHolder) {} // expected-error{{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public func testDefaultArgument(_: Int = NormalProtoAssoc()) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + +public class SubclassOfNormalClass: NormalClass {} + +public func testInheritedConformance(_: NormalProtoAssocHolder) {} // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public func testSpecializedConformance(_: NormalProtoAssocHolder>) {} // expected-error {{cannot use conformance of 'GenericStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +extension Array where Element == NormalProtoAssocHolder { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + public func testConstrainedExtensionUsingBadConformance() {} +} + +public struct ConditionalGenericStruct {} +extension ConditionalGenericStruct: NormalProto where T: NormalProto { + public typealias Assoc = Int +} +public func testConditionalGeneric(_: NormalProtoAssocHolder>) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public protocol PublicInferredAssociatedType { + associatedtype Assoc: NormalProto + func takesAssoc(_: Assoc) +} +@usableFromInline protocol UFIInferredAssociatedType { + associatedtype Assoc: NormalProto + func takesAssoc(_: Assoc) +} +protocol InternalInferredAssociatedType { + associatedtype Assoc: NormalProto + func takesAssoc(_: Assoc) +} + +public struct PublicInferredAssociatedTypeImpl { + public func takesAssoc(_: NormalStruct) {} +} +extension PublicInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} +extension PublicInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} +extension PublicInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay + +@usableFromInline struct UFIInferredAssociatedTypeImpl { + public func takesAssoc(_: NormalStruct) {} +} +extension UFIInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} +extension UFIInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} +extension UFIInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay + +struct InternalInferredAssociatedTypeImpl { + public func takesAssoc(_: NormalStruct) {} +} +extension InternalInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // okay +extension InternalInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // okay +extension InternalInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay + + +public protocol BaseProtoWithNoRequirement { + associatedtype Assoc + func takesAssoc(_: Assoc) +} +public protocol RefinedProto: BaseProtoWithNoRequirement where Assoc: NormalProto { +} + +public struct RefinedProtoImpl: RefinedProto { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: NormalStruct) {} +} + +public protocol RefinedSelfProto where Self: NormalProto {} +extension NormalStruct: RefinedSelfProto {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public protocol RefinedSelfProtoInheritance: NormalProto {} +extension NormalStruct: RefinedSelfProtoInheritance {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + +public protocol SlightlyMoreComplicatedRequirement { + associatedtype Assoc: Collection where Assoc.Element: NormalProto + func takesAssoc(_: Assoc) +} +public struct SlightlyMoreComplicatedRequirementImpl: SlightlyMoreComplicatedRequirement { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc.Element' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: [NormalStruct]) {} +} +public struct RequirementsHandleSubclassesToo: SlightlyMoreComplicatedRequirement { // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' in associated type 'Self.Assoc.Element' (inferred as 'SubclassOfNormalClass'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: [SubclassOfNormalClass]) {} +} + +public struct RequirementsHandleSpecializationsToo: SlightlyMoreComplicatedRequirement { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc.Element' (inferred as 'ConditionalGenericStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: [ConditionalGenericStruct]) {} +} + +public struct ClassConstrainedGenericArg: PublicInferredAssociatedType { // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'T'); 'BADLibrary' has been imported as '@_implementationOnly'}} + public func takesAssoc(_: T) {} +} + + +public protocol RecursiveRequirements { + associatedtype Other: RecursiveRequirements +} +extension GenericStruct: RecursiveRequirements { + public typealias Other = GenericStruct +} +public struct RecursiveRequirementsHolder {} +public func makeSureRecursiveRequirementsDontBreakEverything(_: RecursiveRequirementsHolder>) {} + + +@inlinable func testFunctionBody() { + +} diff --git a/test/Sema/implementation-only-import-library-evolution.swift b/test/Sema/implementation-only-import-library-evolution.swift index 383ae83945e9b..1877162d8a04b 100644 --- a/test/Sema/implementation-only-import-library-evolution.swift +++ b/test/Sema/implementation-only-import-library-evolution.swift @@ -1,5 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule %S/Inputs/implementation-only-import-in-decls-helper.swift +// RUN: %target-swift-frontend -emit-module -o %t/NormalLibrary.swiftmodule %S/Inputs/implementation-only-import-in-decls-public-helper.swift +// RUN: %target-swift-frontend -emit-module -o %t/BADLibrary.swiftmodule %S/Inputs/implementation-only-import-in-decls-helper.swift -I %t // RUN: %target-typecheck-verify-swift -I %t -enable-library-evolution