diff --git a/include/swift/AST/AccessScopeChecker.h b/include/swift/AST/AccessScopeChecker.h index 0e17ca9c08017..608f489cca110 100644 --- a/include/swift/AST/AccessScopeChecker.h +++ b/include/swift/AST/AccessScopeChecker.h @@ -23,47 +23,79 @@ namespace swift { -class AbstractStorageDecl; -class ExtensionDecl; +class BoundGenericType; +class ComponentIdentTypeRepr; +class NominalType; class SourceFile; -class ValueDecl; +class TypeAliasType; class AccessScopeChecker { const SourceFile *File; bool TreatUsableFromInlineAsPublic; -protected: - ASTContext &Context; Optional Scope = AccessScope::getPublic(); AccessScopeChecker(const DeclContext *useDC, bool treatUsableFromInlineAsPublic); - bool visitDecl(ValueDecl *VD); -}; - -class TypeReprAccessScopeChecker : private ASTWalker, AccessScopeChecker { - TypeReprAccessScopeChecker(const DeclContext *useDC, - bool treatUsableFromInlineAsPublic); - - bool walkToTypeReprPre(TypeRepr *TR) override; - bool walkToTypeReprPost(TypeRepr *TR) override; + bool visitDecl(const ValueDecl *VD); public: static Optional getAccessScope(TypeRepr *TR, const DeclContext *useDC, bool treatUsableFromInlineAsPublic = false); + static Optional + getAccessScope(Type T, const DeclContext *useDC, + bool treatUsableFromInlineAsPublic = false); }; -class TypeAccessScopeChecker : private TypeWalker, AccessScopeChecker { - TypeAccessScopeChecker(const DeclContext *useDC, - bool treatUsableFromInlineAsPublic); +/// Walks a Type to find all NominalTypes, BoundGenericTypes, and +/// TypeAliasTypes. +class TypeDeclFinder : public TypeWalker { + Action walkToTypePre(Type T) override; + +public: + virtual Action visitNominalType(const NominalType *ty) { + return Action::Continue; + } + virtual Action visitBoundGenericType(const BoundGenericType *ty) { + return Action::Continue; + } + virtual Action visitTypeAliasType(const TypeAliasType *ty) { + return Action::Continue; + } +}; - Action walkToTypePre(Type T); +/// A TypeDeclFinder for use cases where all types should be treated +/// equivalently and where generic arguments can be walked to separately from +/// the generic type. +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; public: - static Optional - getAccessScope(Type T, const DeclContext *useDC, - bool treatUsableFromInlineAsPublic = false); + explicit SimpleTypeDeclFinder( + llvm::function_ref callback) + : Callback(callback) {} +}; + +/// Walks a TypeRepr to find all ComponentIdentTypeReprs with bound TypeDecls. +/// +/// Subclasses can either override #visitTypeDecl if they only care about +/// types on their own, or #visitComponentIdentTypeRepr if they want to keep +/// the TypeRepr around. +class TypeReprIdentFinder : public ASTWalker { + /// The function to call when a ComponentIdentTypeRepr is seen. + llvm::function_ref Callback; + + bool walkToTypeReprPost(TypeRepr *TR) override; +public: + explicit TypeReprIdentFinder( + llvm::function_ref callback) + : Callback(callback) {} }; } diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8e8349d9e77de..edaab71ceb385 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2397,6 +2397,11 @@ NOTE(enum_raw_value_incrementing_from_zero,none, NOTE(construct_raw_representable_from_unwrapped_value,none, "construct %0 from unwrapped %1 value", (Type, Type)) +ERROR(decl_from_implementation_only_module,none, + "cannot use %0 here; %1 has been imported as " + "'@_implementationOnly'", + (DeclName, Identifier)) + // Derived conformances ERROR(cannot_synthesize_init_in_extension_of_nonfinal,none, "implementation of %0 for non-final class cannot be automatically " diff --git a/lib/AST/AccessScopeChecker.cpp b/lib/AST/AccessScopeChecker.cpp index 2cc38cbeed1b5..66df111402add 100644 --- a/lib/AST/AccessScopeChecker.cpp +++ b/lib/AST/AccessScopeChecker.cpp @@ -24,12 +24,11 @@ using namespace swift; AccessScopeChecker::AccessScopeChecker(const DeclContext *useDC, bool treatUsableFromInlineAsPublic) : File(useDC->getParentSourceFile()), - TreatUsableFromInlineAsPublic(treatUsableFromInlineAsPublic), - Context(File->getASTContext()) {} + TreatUsableFromInlineAsPublic(treatUsableFromInlineAsPublic) {} bool -AccessScopeChecker::visitDecl(ValueDecl *VD) { - if (!VD || isa(VD)) +AccessScopeChecker::visitDecl(const ValueDecl *VD) { + if (isa(VD)) return true; auto AS = VD->getFormalAccessScope(File, TreatUsableFromInlineAsPublic); @@ -37,55 +36,64 @@ AccessScopeChecker::visitDecl(ValueDecl *VD) { return Scope.hasValue(); } -TypeReprAccessScopeChecker::TypeReprAccessScopeChecker(const DeclContext *useDC, - bool treatUsableFromInlineAsPublic) - : AccessScopeChecker(useDC, treatUsableFromInlineAsPublic) { +bool TypeReprIdentFinder::walkToTypeReprPost(TypeRepr *TR) { + auto CITR = dyn_cast(TR); + if (!CITR || !CITR->getBoundDecl()) + return true; + return Callback(CITR); } -bool -TypeReprAccessScopeChecker::walkToTypeReprPre(TypeRepr *TR) { - if (auto CITR = dyn_cast(TR)) - return visitDecl(CITR->getBoundDecl()); - return true; +Optional +AccessScopeChecker::getAccessScope(TypeRepr *TR, const DeclContext *useDC, + bool treatUsableFromInlineAsPublic) { + AccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic); + TR->walk(TypeReprIdentFinder([&](const ComponentIdentTypeRepr *typeRepr) { + return checker.visitDecl(typeRepr->getBoundDecl()); + })); + return checker.Scope; } -bool -TypeReprAccessScopeChecker::walkToTypeReprPost(TypeRepr *TR) { - return Scope.hasValue(); -} +TypeWalker::Action TypeDeclFinder::walkToTypePre(Type T) { + if (auto *TAT = dyn_cast(T.getPointer())) + return visitTypeAliasType(TAT); -Optional -TypeReprAccessScopeChecker::getAccessScope(TypeRepr *TR, const DeclContext *useDC, - bool treatUsableFromInlineAsPublic) { - TypeReprAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic); - TR->walk(checker); - return checker.Scope; + // FIXME: We're looking through sugar here so that we visit, e.g., + // Swift.Array when we see `[Int]`. But that means we do redundant work when + // we see sugar that's purely structural, like `(Int)`. Fortunately, paren + // types are the only such purely structural sugar at the time this comment + // was written, and they're not so common in the first place. + if (auto *BGT = T->getAs()) + return visitBoundGenericType(BGT); + if (auto *NT = T->getAs()) + return visitNominalType(NT); + + return Action::Continue; } -TypeAccessScopeChecker::TypeAccessScopeChecker(const DeclContext *useDC, - bool treatUsableFromInlineAsPublic) - : AccessScopeChecker(useDC, treatUsableFromInlineAsPublic) {} +TypeWalker::Action +SimpleTypeDeclFinder::visitNominalType(const NominalType *ty) { + return Callback(ty->getDecl()); +} TypeWalker::Action -TypeAccessScopeChecker::walkToTypePre(Type T) { - ValueDecl *VD; - if (auto *BNAD = dyn_cast(T.getPointer())) - VD = BNAD->getDecl(); - else if (auto *NTD = T->getAnyNominal()) - VD = NTD; - else - VD = nullptr; - - if (!visitDecl(VD)) - return Action::Stop; +SimpleTypeDeclFinder::visitBoundGenericType(const BoundGenericType *ty) { + return Callback(ty->getDecl()); +} - return Action::Continue; +TypeWalker::Action +SimpleTypeDeclFinder::visitTypeAliasType(const TypeAliasType *ty) { + return Callback(ty->getDecl()); } + Optional -TypeAccessScopeChecker::getAccessScope(Type T, const DeclContext *useDC, - bool treatUsableFromInlineAsPublic) { - TypeAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic); - T.walk(checker); +AccessScopeChecker::getAccessScope(Type T, const DeclContext *useDC, + bool treatUsableFromInlineAsPublic) { + AccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic); + T.walk(SimpleTypeDeclFinder([&](const ValueDecl *VD) { + if (checker.visitDecl(VD)) + return TypeWalker::Action::Continue; + return TypeWalker::Action::Stop; + })); return checker.Scope; } diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index cd1d4b494c4f7..faf5e8490d94e 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -39,6 +39,33 @@ enum class DowngradeToWarning: bool { Yes }; +/// Calls \p callback for each type in each requirement provided by +/// \p source. +static void forAllRequirementTypes( + WhereClauseOwner source, + llvm::function_ref callback) { + RequirementRequest::visitRequirements( + source, TypeResolutionStage::Interface, + [&](const Requirement &req, RequirementRepr* reqRepr) { + switch (req.getKind()) { + case RequirementKind::Conformance: + case RequirementKind::SameType: + case RequirementKind::Superclass: + callback(req.getFirstType(), + RequirementRepr::getFirstTypeRepr(reqRepr)); + callback(req.getSecondType(), + RequirementRepr::getSecondTypeRepr(reqRepr)); + break; + + case RequirementKind::Layout: + callback(req.getFirstType(), + RequirementRepr::getFirstTypeRepr(reqRepr)); + break; + } + return false; + }); +} + /// \see checkTypeAccess using CheckTypeAccessCallback = void(AccessScope, const TypeRepr *, DowngradeToWarning); @@ -69,7 +96,12 @@ class AccessControlCheckerBase { WhereClauseOwner source, AccessScope accessScope, const DeclContext *useDC, - llvm::function_ref diagnose); + llvm::function_ref diagnose) { + forAllRequirementTypes(source, [&](Type type, TypeRepr *typeRepr) { + checkTypeAccessImpl(type, typeRepr, accessScope, useDC, + /*mayBeInferred*/false, diagnose); + }); + } AccessControlCheckerBase(TypeChecker &TC, bool checkUsableFromInline) : TC(TC), checkUsableFromInline(checkUsableFromInline) {} @@ -171,8 +203,7 @@ void AccessControlCheckerBase::checkTypeAccessImpl( AccessScope problematicAccessScope = AccessScope::getPublic(); if (type) { Optional typeAccessScope = - TypeAccessScopeChecker::getAccessScope(type, useDC, - checkUsableFromInline); + AccessScopeChecker::getAccessScope(type, useDC, checkUsableFromInline); // Note: This means that the type itself is invalid for this particular // context, because it references declarations from two incompatible scopes. @@ -193,8 +224,8 @@ void AccessControlCheckerBase::checkTypeAccessImpl( return; Optional typeReprAccessScope = - TypeReprAccessScopeChecker::getAccessScope(typeRepr, useDC, - checkUsableFromInline); + AccessScopeChecker::getAccessScope(typeRepr, useDC, + checkUsableFromInline); if (!typeReprAccessScope.hasValue()) return; @@ -221,8 +252,8 @@ void AccessControlCheckerBase::checkTypeAccessImpl( // // Downgrade the error to a warning in this case for source compatibility. Optional typeReprAccessScope = - TypeReprAccessScopeChecker::getAccessScope(typeRepr, useDC, - checkUsableFromInline); + AccessScopeChecker::getAccessScope(typeRepr, useDC, + checkUsableFromInline); assert(typeReprAccessScope && "valid Type but not valid TypeRepr?"); if (contextAccessScope.hasEqualDeclContextWith(*typeReprAccessScope) || contextAccessScope.isChildOf(*typeReprAccessScope)) { @@ -281,39 +312,6 @@ static void highlightOffendingType(TypeChecker &TC, InFlightDiagnostic &diag, } } -void AccessControlCheckerBase::checkRequirementAccess( - WhereClauseOwner source, - AccessScope accessScope, - const DeclContext *useDC, - llvm::function_ref diagnose) { - RequirementRequest::visitRequirements( - source, TypeResolutionStage::Interface, - [&](const Requirement &req, RequirementRepr* reqRepr) { - switch (req.getKind()) { - case RequirementKind::Conformance: - case RequirementKind::SameType: - case RequirementKind::Superclass: - checkTypeAccessImpl(req.getFirstType(), - RequirementRepr::getFirstTypeRepr(reqRepr), - accessScope, useDC, /*mayBeInferred*/false, - diagnose); - checkTypeAccessImpl(req.getSecondType(), - RequirementRepr::getSecondTypeRepr(reqRepr), - accessScope, useDC, /*mayBeInferred*/false, - diagnose); - break; - - case RequirementKind::Layout: - checkTypeAccessImpl(req.getFirstType(), - RequirementRepr::getFirstTypeRepr(reqRepr), - accessScope, useDC, /*mayBeInferred*/false, - diagnose); - break; - } - return false; - }); -} - void AccessControlCheckerBase::checkGenericParamAccess( const GenericParamList *params, const Decl *owner, @@ -441,6 +439,8 @@ class AccessControlChecker : public AccessControlCheckerBase, UNREACHABLE(PrecedenceGroup, "cannot appear in a type context") UNREACHABLE(Module, "cannot appear in a type context") + UNREACHABLE(IfConfig, "does not have access control") + UNREACHABLE(PoundDiagnostic, "does not have access control") UNREACHABLE(Param, "does not have access control") UNREACHABLE(GenericTypeParam, "does not have access control") UNREACHABLE(MissingMember, "does not have access control") @@ -449,8 +449,6 @@ class AccessControlChecker : public AccessControlCheckerBase, #define UNINTERESTING(KIND) \ void visit##KIND##Decl(KIND##Decl *D) {} - UNINTERESTING(IfConfig) // Does not have access control. - UNINTERESTING(PoundDiagnostic) // Does not have access control. UNINTERESTING(EnumCase) // Handled at the EnumElement level. UNINTERESTING(Var) // Handled at the PatternBinding level. UNINTERESTING(Destructor) // Always correct. @@ -1401,16 +1399,382 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, } } }; + +class ImplementationOnlyImportChecker + : public DeclVisitor { + using CheckImplementationOnlyCallback = + llvm::function_ref; + + TypeChecker &TC; + + void checkTypeImpl(Type type, const TypeRepr *typeRepr, const SourceFile *SF, + CheckImplementationOnlyCallback diagnose) { + // Don't bother checking errors. + if (type && type->hasError()) + return; + + bool foundAnyIssues = false; + + // Check the TypeRepr first (if present), because that will give us a + // better diagonstic. + if (typeRepr) { + const_cast(typeRepr)->walk(TypeReprIdentFinder( + [&](const ComponentIdentTypeRepr *component) { + ModuleDecl *M = component->getBoundDecl()->getModuleContext(); + if (!SF->isImportedImplementationOnly(M)) + return true; + + diagnose(component->getBoundDecl(), component); + foundAnyIssues = true; + // We still continue even in the diagnostic case to report multiple + // violations. + return true; + })); + } + + // 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) { + ModuleDecl *M = typeDecl->getModuleContext(); + if (!SF->isImportedImplementationOnly(M)) + return TypeWalker::Action::Continue; + + diagnose(typeDecl, /*typeRepr*/nullptr); + // We still continue even in the diagnostic case to report multiple + // violations. + return TypeWalker::Action::Continue; + })); + } + } + + void checkType(Type type, const TypeRepr *typeRepr, const Decl *context, + CheckImplementationOnlyCallback diagnose) { + auto *SF = context->getDeclContext()->getParentSourceFile(); + assert(SF && "checking a non-source declaration?"); + return checkTypeImpl(type, typeRepr, SF, diagnose); + } + + void checkType(const TypeLoc &TL, const Decl *context, + CheckImplementationOnlyCallback diagnose) { + checkType(TL.getType(), TL.getTypeRepr(), context, diagnose); + } + + void checkGenericParams(const GenericParamList *params, + const Decl *owner) { + if (!params) + return; + + for (auto param : *params) { + if (param->getInherited().empty()) + continue; + assert(param->getInherited().size() == 1); + checkType(param->getInherited().front(), owner, + getDiagnoseCallback(owner)); + } + + forAllRequirementTypes(WhereClauseOwner( + owner->getInnermostDeclContext(), + const_cast(params)), + [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, owner, getDiagnoseCallback(owner)); + }); + } + + class DiagnoseGenerically { + TypeChecker &TC; + const Decl *D; + public: + DiagnoseGenerically(TypeChecker &TC, const Decl *D) : TC(TC), D(D) {} + + void operator()(const TypeDecl *offendingType, + const TypeRepr *complainRepr) { + ModuleDecl *M = offendingType->getModuleContext(); + auto diag = TC.diagnose(D, diag::decl_from_implementation_only_module, + offendingType->getFullName(), M->getName()); + highlightOffendingType(TC, diag, complainRepr); + } + + static_assert(std::is_convertible::value, + "DiagnoseGenerically has wrong call signature"); + }; + + DiagnoseGenerically getDiagnoseCallback(const Decl *D) { + return DiagnoseGenerically(TC, D); + } + +public: + explicit ImplementationOnlyImportChecker(TypeChecker &TC) : TC(TC) {} + + static bool shouldSkipChecking(const ValueDecl *VD) { + // Is this part of the module's API or ABI? + AccessScope accessScope = + VD->getFormalAccessScope(nullptr, + /*treatUsableFromInlineAsPublic*/true); + if (accessScope.isPublic()) + return false; + + // Is this a stored property in a non-resilient struct or class? + auto *property = dyn_cast(VD); + if (!property || !property->hasStorage() || property->isStatic()) + return true; + auto *parentNominal = dyn_cast(property->getDeclContext()); + if (!parentNominal || parentNominal->isResilient()) + return true; + + // Is that struct or class part of the module's API or ABI? + AccessScope parentAccessScope = parentNominal->getFormalAccessScope( + nullptr, /*treatUsableFromInlineAsPublic*/true); + if (parentAccessScope.isPublic()) + return false; + + return true; + } + + void visit(Decl *D) { + if (D->isInvalid() || D->isImplicit()) + return; + + if (auto *VD = dyn_cast(D)) + if (shouldSkipChecking(VD)) + return; + + DeclVisitor::visit(D); + + if (auto *extension = dyn_cast(D->getDeclContext())) { + checkType(extension->getExtendedTypeLoc(), extension, + getDiagnoseCallback(extension)); + checkConstrainedExtensionRequirements(extension); + } + } + + // Force all kinds to be handled at a lower level. + void visitDecl(Decl *D) = delete; + void visitValueDecl(ValueDecl *D) = delete; + +#define UNREACHABLE(KIND, REASON) \ + void visit##KIND##Decl(KIND##Decl *D) { \ + llvm_unreachable(REASON); \ + } + UNREACHABLE(Import, "not applicable") + UNREACHABLE(TopLevelCode, "not applicable") + UNREACHABLE(Module, "not applicable") + + UNREACHABLE(Param, "handled by the enclosing declaration") + UNREACHABLE(GenericTypeParam, "handled by the enclosing declaration") + UNREACHABLE(MissingMember, "handled by the enclosing declaration") +#undef UNREACHABLE + +#define UNINTERESTING(KIND) \ + void visit##KIND##Decl(KIND##Decl *D) {} + + UNINTERESTING(PrefixOperator) // Does not reference other decls. + UNINTERESTING(PostfixOperator) // Does not reference other decls. + UNINTERESTING(IfConfig) // Not applicable. + UNINTERESTING(PoundDiagnostic) // Not applicable. + UNINTERESTING(EnumCase) // Handled at the EnumElement level. + UNINTERESTING(Destructor) // Always correct. + UNINTERESTING(Accessor) // Handled by the Var or Subscript. + + // Handled at the PatternBinding level; if the pattern has a simple + // "name: TheType" form, we can get better results by diagnosing the TypeRepr. + UNINTERESTING(Var) + + /// \see visitPatternBindingDecl + void checkNamedPattern(const NamedPattern *NP, + const llvm::DenseSet &seenVars) { + const VarDecl *theVar = NP->getDecl(); + if (shouldSkipChecking(theVar)) + return; + // Only check individual variables if we didn't check an enclosing + // TypedPattern. + if (seenVars.count(theVar) || theVar->isInvalid()) + return; + + checkType(theVar->getInterfaceType(), /*typeRepr*/nullptr, theVar, + getDiagnoseCallback(theVar)); + } + + /// \see visitPatternBindingDecl + void checkTypedPattern(const TypedPattern *TP, + llvm::DenseSet &seenVars) { + // FIXME: We need to figure out if this is a stored or computed property, + // so we pull out some random VarDecl in the pattern. They're all going to + // be the same, but still, ick. + const VarDecl *anyVar = nullptr; + TP->forEachVariable([&](VarDecl *V) { + seenVars.insert(V); + anyVar = V; + }); + if (!anyVar) + return; + if (shouldSkipChecking(anyVar)) + return; + + checkType(TP->getTypeLoc(), anyVar, getDiagnoseCallback(anyVar)); + } + + void visitPatternBindingDecl(PatternBindingDecl *PBD) { + llvm::DenseSet seenVars; + for (auto entry : PBD->getPatternList()) { + entry.getPattern()->forEachNode([&](const Pattern *P) { + if (auto *NP = dyn_cast(P)) { + checkNamedPattern(NP, seenVars); + return; + } + + auto *TP = dyn_cast(P); + if (!TP) + return; + checkTypedPattern(TP, seenVars); + }); + seenVars.clear(); + } + } + + void visitTypeAliasDecl(TypeAliasDecl *TAD) { + checkGenericParams(TAD->getGenericParams(), TAD); + checkType(TAD->getUnderlyingTypeLoc(), TAD, getDiagnoseCallback(TAD)); + } + + void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { + llvm::for_each(assocType->getInherited(), + [&](TypeLoc requirement) { + checkType(requirement, assocType, getDiagnoseCallback(assocType)); + }); + checkType(assocType->getDefaultDefinitionLoc(), assocType, + getDiagnoseCallback(assocType)); + + if (assocType->getTrailingWhereClause()) { + forAllRequirementTypes(assocType, + [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, assocType, getDiagnoseCallback(assocType)); + }); + } + } + + void visitNominalTypeDecl(const NominalTypeDecl *nominal) { + checkGenericParams(nominal->getGenericParams(), nominal); + + llvm::for_each(nominal->getInherited(), + [&](TypeLoc nextInherited) { + checkType(nextInherited, nominal, getDiagnoseCallback(nominal)); + }); + } + + void visitProtocolDecl(ProtocolDecl *proto) { + llvm::for_each(proto->getInherited(), + [&](TypeLoc requirement) { + checkType(requirement, proto, getDiagnoseCallback(proto)); + }); + + if (proto->getTrailingWhereClause()) { + forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, proto, getDiagnoseCallback(proto)); + }); + } + } + + 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)); + } + + void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { + checkGenericParams(fn->getGenericParams(), fn); + + for (auto *P : *fn->getParameters()) + checkType(P->getTypeLoc(), fn, getDiagnoseCallback(fn)); + } + + void visitFuncDecl(FuncDecl *FD) { + visitAbstractFunctionDecl(FD); + checkType(FD->getBodyResultTypeLoc(), FD, getDiagnoseCallback(FD)); + } + + void visitEnumElementDecl(EnumElementDecl *EED) { + if (!EED->hasAssociatedValues()) + return; + for (auto &P : *EED->getParameterList()) + checkType(P->getTypeLoc(), EED, getDiagnoseCallback(EED)); + } + + void checkConstrainedExtensionRequirements(ExtensionDecl *ED) { + if (!ED->getTrailingWhereClause()) + return; + forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) { + checkType(type, typeRepr, ED, getDiagnoseCallback(ED)); + }); + } + + void visitExtensionDecl(ExtensionDecl *ED) { + if (shouldSkipChecking(ED->getExtendedNominal())) + return; + + // FIXME: We should allow conforming to implementation-only protocols, + // but just hide that from interfaces. + llvm::for_each(ED->getInherited(), + [&](TypeLoc nextInherited) { + checkType(nextInherited, ED, getDiagnoseCallback(ED)); + }); + + if (!ED->getInherited().empty()) + checkConstrainedExtensionRequirements(ED); + } + + void checkPrecedenceGroup(const PrecedenceGroupDecl *PGD, + const Decl *refDecl, SourceLoc diagLoc, + SourceRange refRange) { + const SourceFile *SF = refDecl->getDeclContext()->getParentSourceFile(); + ModuleDecl *M = PGD->getModuleContext(); + if (!SF->isImportedImplementationOnly(M)) + return; + + auto diag = TC.diagnose(diagLoc, diag::decl_from_implementation_only_module, + PGD->getName(), M->getName()); + if (refRange.isValid()) + diag.highlight(refRange); + diag.flush(); + TC.diagnose(PGD, diag::decl_declared_here, PGD->getName()); + } + + void visitInfixOperatorDecl(InfixOperatorDecl *IOD) { + // FIXME: Handle operator designated types (which also applies to prefix + // and postfix operators). + if (auto *precedenceGroup = IOD->getPrecedenceGroup()) { + if (!IOD->getIdentifierLocs().empty()) { + checkPrecedenceGroup(precedenceGroup, IOD, IOD->getLoc(), + IOD->getIdentifierLocs().front()); + } + } + } + + void visitPrecedenceGroupDecl(PrecedenceGroupDecl *PGD) { + llvm::for_each(PGD->getLowerThan(), + [&](const PrecedenceGroupDecl::Relation &relation) { + checkPrecedenceGroup(relation.Group, PGD, PGD->getLowerThanLoc(), + relation.NameLoc); + }); + llvm::for_each(PGD->getHigherThan(), + [&](const PrecedenceGroupDecl::Relation &relation) { + checkPrecedenceGroup(relation.Group, PGD, PGD->getHigherThanLoc(), + relation.NameLoc); + }); + } +}; } // end anonymous namespace -void swift::checkAccessControl(TypeChecker &TC, Decl *D) { - AccessControlChecker(TC).visit(D); - UsableFromInlineChecker(TC).visit(D); -} +static void checkExtensionGenericParamAccess(TypeChecker &TC, + const ExtensionDecl *ED) { + auto *AA = ED->getAttrs().getAttribute(); + if (!AA) + return; + AccessLevel userSpecifiedAccess = AA->getAccess(); -void swift::checkExtensionGenericParamAccess(TypeChecker &TC, - const ExtensionDecl *ED, - AccessLevel userSpecifiedAccess) { AccessScope desiredAccessScope = AccessScope::getPublic(); switch (userSpecifiedAccess) { case AccessLevel::Private: @@ -1436,3 +1800,14 @@ void swift::checkExtensionGenericParamAccess(TypeChecker &TC, desiredAccessScope, userSpecifiedAccess); } + +void swift::checkAccessControl(TypeChecker &TC, Decl *D) { + if (isa(D) || isa(D)) { + AccessControlChecker(TC).visit(D); + UsableFromInlineChecker(TC).visit(D); + } else if (auto *ED = dyn_cast(D)) { + checkExtensionGenericParamAccess(TC, ED); + } + + ImplementationOnlyImportChecker(TC).visit(D); +} diff --git a/lib/Sema/TypeCheckAccess.h b/lib/Sema/TypeCheckAccess.h index 453567bc48a47..0c8bb4b870062 100644 --- a/lib/Sema/TypeCheckAccess.h +++ b/lib/Sema/TypeCheckAccess.h @@ -22,18 +22,13 @@ namespace swift { class Decl; class TypeChecker; -/// Checks the given declaration's signature does not reference any other -/// declarations that are less visible than the declaration itself. +/// Performs access-related checks for \p D. /// -/// \p D must be a ValueDecl or a Decl that can appear in a type context. +/// At a high level, this checks the given declaration's signature does not +/// reference any other declarations that are less visible than the declaration +/// itself. Related checks may also be performed. void checkAccessControl(TypeChecker &TC, Decl *D); -/// Checks that the generic parameters of the given extension do not reference -/// any other declarations that are less visible than the user-specified access -/// on the extension. -void checkExtensionGenericParamAccess(TypeChecker &TC, const ExtensionDecl *ED, - AccessLevel userSpecifiedAccess); - } // end namespace swift #endif diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 4ff9ee787389e..eeb2ac400c734 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2082,10 +2082,12 @@ class DeclChecker : public DeclVisitor { void visitOperatorDecl(OperatorDecl *OD) { TC.validateDecl(OD); + checkAccessControl(TC, OD); } void visitPrecedenceGroupDecl(PrecedenceGroupDecl *PGD) { TC.validateDecl(PGD); + checkAccessControl(TC, PGD); } void visitMissingMemberDecl(MissingMemberDecl *MMD) { @@ -2984,8 +2986,7 @@ class DeclChecker : public DeclVisitor { if (!ED->isInvalid()) TC.checkDeclAttributes(ED); - if (auto *AA = ED->getAttrs().getAttribute()) - checkExtensionGenericParamAccess(TC, ED, AA->getAccess()); + checkAccessControl(TC, ED); // Trigger the creation of all of the conformances associated with this // nominal type. diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 003a8e91c9d4b..0e3ae30166a89 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -2523,8 +2523,8 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType, // when the underlying type has sufficient access, but only in // non-resilient modules. Optional underlyingTypeScope = - TypeAccessScopeChecker::getAccessScope(type, DC, - /*usableFromInline*/false); + AccessScopeChecker::getAccessScope(type, DC, + /*usableFromInline*/false); assert(underlyingTypeScope.hasValue() && "the type is already invalid and we shouldn't have gotten here"); diff --git a/test/Sema/Inputs/implementation-only-import-in-decls-helper.swift b/test/Sema/Inputs/implementation-only-import-in-decls-helper.swift new file mode 100644 index 0000000000000..4ef1dd4dfd9fe --- /dev/null +++ b/test/Sema/Inputs/implementation-only-import-in-decls-helper.swift @@ -0,0 +1,9 @@ +public struct BadStruct {} +public protocol BadProto {} +open class BadClass {} + +public struct IntLike: ExpressibleByIntegerLiteral, Equatable { + public init(integerLiteral: Int) {} +} + +precedencegroup BadPrecedence {} diff --git a/test/Sema/implementation-only-import-in-decls.swift b/test/Sema/implementation-only-import-in-decls.swift new file mode 100644 index 0000000000000..a24126cdef056 --- /dev/null +++ b/test/Sema/implementation-only-import-in-decls.swift @@ -0,0 +1,150 @@ +// 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-typecheck-verify-swift -I %t + +@_implementationOnly import BADLibrary + +public struct TestConformance: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +@usableFromInline struct TestConformanceUFI: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +struct TestConformanceOkay: BadProto {} // ok + +public class TestConformanceClass: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public enum TestConformanceEnum: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + +public struct TestGenericParams {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public struct TestGenericParamsWhereClause where T: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public enum TestCase { + case x(BadStruct) // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + case y(Int, BadStruct) // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +public func testGenericParams(_: T) {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public func testGenericParamsWhereClause(_: T) where T: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public func testParams(_: BadStruct, _: BadProto) {} +// expected-error@-1 {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +// expected-error@-2 {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public func testResult() -> BadStruct? { return nil } // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public struct TestSubscript { + public subscript(_: T) -> Int { return 0 } // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + public subscript(where _: T) -> Int where T: BadProto { return 0 } // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + public subscript(_: BadStruct) -> Int { return 0 } // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + public subscript(_: Int) -> BadStruct? { return nil } // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +public struct TestInit { + public init(_: BadStruct) {} // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + public init(_: T) {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + public init(where _: T) where T: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +public protocol TestInherited: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public protocol TestConstraintBase { + associatedtype Assoc +} +public protocol TestConstraint: TestConstraintBase where Assoc: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public protocol TestConstraintEq: TestConstraintBase where Assoc == BadStruct {} // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public protocol TestAssocType { + associatedtype Assoc: BadProto // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +public protocol TestAssocTypeWhereClause { + associatedtype Assoc: Collection where Assoc.Element: BadProto // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +public enum TestRawType: IntLike { // expected-error {{cannot use 'IntLike' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + case x = 1 +} + +public class TestSubclass: BadClass { // expected-error {{cannot use 'BadClass' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +public typealias TestUnderlying = BadStruct // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public typealias TestGenericParamsAliasWhereClause = T where T: BadProto // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public typealias TestGenericParamsAlias = T // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +public var testBadType: BadStruct? = nil // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public var testBadTypeInferred = Optional.none // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public var testBadTypePartiallyInferred: Optional = Optional.none // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public var (testBadTypeTuple1, testBadTypeTuple2): (BadStruct?, BadClass?) = (nil, nil) +// expected-error@-1 {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +// expected-error@-2 {{cannot use 'BadClass' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public var (testBadTypeTuplePartlyInferred1, testBadTypeTuplePartlyInferred2): (Optional, Optional) = (Optional.none, Optional.none) // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public var (testBadTypeTuplePartlyInferred3, testBadTypeTuplePartlyInferred4): (Optional, Optional) = (Optional.none, Optional.none) // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public var testMultipleBindings1: Int? = nil, testMultipleBindings2: BadStruct? = nil // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +public var testMultipleBindings3: BadStruct? = nil, testMultipleBindings4: Int? = nil // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +extension BadStruct { // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + public func testExtensionOfBadType() {} // FIXME: Should complain here instead of at the extension decl. +} +extension BadStruct { + func testExtensionOfOkayType() {} +} + +extension Array where Element == BadStruct { // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + public func testExtensionWithBadRequirement() {} // FIXME: Should complain here instead of at the extension decl. +} + +extension Array where Element == BadStruct { + func testExtensionWithOkayRequirement() {} // okay +} + +extension Int: BadProto {} // expected-error {{cannot use 'BadProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +struct TestExtensionConformanceOkay {} +extension TestExtensionConformanceOkay: BadProto {} // okay + +public protocol TestConstrainedExtensionProto {} +extension Array: TestConstrainedExtensionProto where Element == BadStruct { // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + + +infix operator !!!!!!: BadPrecedence // expected-error {{cannot use 'BadPrecedence' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + +precedencegroup TestLowerThan { + lowerThan: BadPrecedence // expected-error {{cannot use 'BadPrecedence' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} +precedencegroup TestHigherThan { + higherThan: BadPrecedence // expected-error {{cannot use 'BadPrecedence' here; 'BADLibrary' has been imported as '@_implementationOnly'}} +} + +public struct PublicStructStoredProperties { + public var publiclyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + internal var internallyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private var privatelyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private let letIsLikeVar = [BadStruct]() // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + private var computedIsOkay: BadStruct? { return nil } // okay + 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'}} +} + +@usableFromInline internal struct UFIStructStoredProperties { + @usableFromInline var publiclyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + internal var internallyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private var privatelyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private let letIsLikeVar = [BadStruct]() // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + private var computedIsOkay: BadStruct? { return nil } // okay + 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 class PublicClassStoredProperties { + public var publiclyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + internal var internallyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private var privatelyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private let letIsLikeVar = [BadStruct]() // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + private var computedIsOkay: BadStruct? { return nil } // okay + 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'}} +} diff --git a/test/Sema/implementation-only-import-library-evolution.swift b/test/Sema/implementation-only-import-library-evolution.swift new file mode 100644 index 0000000000000..383ae83945e9b --- /dev/null +++ b/test/Sema/implementation-only-import-library-evolution.swift @@ -0,0 +1,77 @@ +// 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-typecheck-verify-swift -I %t -enable-library-evolution + +@_implementationOnly import BADLibrary + +public struct PublicStructStoredProperties { + public var publiclyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + internal var internallyBad: BadStruct? // okay + private var privatelyBad: BadStruct? // okay + private let letIsLikeVar = [BadStruct]() // okay + + private var computedIsOkay: BadStruct? { return nil } // okay + 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'}} +} + +@usableFromInline internal struct UFIStructStoredProperties { + @usableFromInline var publiclyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + internal var internallyBad: BadStruct? // okay + private var privatelyBad: BadStruct? // okay + private let letIsLikeVar = [BadStruct]() // okay + + private var computedIsOkay: BadStruct? { return nil } // okay + 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 class PublicClassStoredProperties { + public var publiclyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + internal var internallyBad: BadStruct? // okay + private var privatelyBad: BadStruct? // okay + private let letIsLikeVar = [BadStruct]() // okay + + private var computedIsOkay: BadStruct? { return nil } // okay + 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'}} +} + +// MARK: Frozen types + +@_fixed_layout +public struct FrozenPublicStructStoredProperties { + public var publiclyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + internal var internallyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private var privatelyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private let letIsLikeVar: [BadStruct] = [] // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + private var computedIsOkay: BadStruct? { return nil } // okay + 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'}} +} + +@_fixed_layout +@usableFromInline internal struct FrozenUFIStructStoredProperties { + @usableFromInline var publiclyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + internal var internallyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private var privatelyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private let letIsLikeVar: [BadStruct] = [] // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + private var computedIsOkay: BadStruct? { return nil } // okay + 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'}} +} + +@_fixed_layout +public class FrozenPublicClassStoredProperties { + public var publiclyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + internal var internallyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private var privatelyBad: BadStruct? // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + private let letIsLikeVar: [BadStruct] = [] // expected-error {{cannot use 'BadStruct' here; 'BADLibrary' has been imported as '@_implementationOnly'}} + + private var computedIsOkay: BadStruct? { return nil } // okay + 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'}} +}