//===--- ResilienceDiagnostics.cpp - Resilience Inlineability Diagnostics -===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements diagnostics for @inlinable. // //===----------------------------------------------------------------------===// #include "TypeChecker.h" #include "swift/AST/Attr.h" #include "swift/AST/Decl.h" #include "swift/AST/Initializer.h" #include "swift/AST/DeclContext.h" using namespace swift; using FragileFunctionKind = TypeChecker::FragileFunctionKind; std::pair TypeChecker::getFragileFunctionKind(const DeclContext *DC) { for (; DC->isLocalContext(); DC = DC->getParent()) { if (isa(DC)) { // Default argument generators of public functions cannot reference // @usableFromInline declarations; all other fragile function kinds // can. auto *VD = cast(DC->getInnermostDeclarationDeclContext()); auto access = VD->getFormalAccessScope(/*useDC=*/nullptr, /*treatUsableFromInlineAsPublic=*/false); return std::make_pair(FragileFunctionKind::DefaultArgument, !access.isPublic()); } if (isa(DC)) return std::make_pair(FragileFunctionKind::PropertyInitializer, /*treatUsableFromInlineAsPublic=*/true); if (auto *AFD = dyn_cast(DC)) { // If the function is a nested function, we will serialize its body if // we serialize the parent's body. if (AFD->getDeclContext()->isLocalContext()) continue; // Bodies of public transparent and always-inline functions are // serialized, so use conservative access patterns. if (AFD->isTransparent()) return std::make_pair(FragileFunctionKind::Transparent, /*treatUsableFromInlineAsPublic=*/true); if (AFD->getAttrs().hasAttribute()) return std::make_pair(FragileFunctionKind::Inlinable, /*treatUsableFromInlineAsPublic=*/true); if (auto attr = AFD->getAttrs().getAttribute()) if (attr->getKind() == InlineKind::Always) return std::make_pair(FragileFunctionKind::InlineAlways, /*treatUsableFromInlineAsPublic=*/true); // If a property or subscript is @inlinable, the accessors are // @inlinable also. if (auto accessor = dyn_cast(AFD)) if (accessor->getStorage()->getAttrs().getAttribute()) return std::make_pair(FragileFunctionKind::Inlinable, /*treatUsableFromInlineAsPublic=*/true); } } llvm_unreachable("Context is not nested inside a fragile function"); } void TypeChecker::diagnoseInlinableLocalType(const NominalTypeDecl *NTD) { auto *DC = NTD->getDeclContext(); auto expansion = DC->getResilienceExpansion(); if (expansion == ResilienceExpansion::Minimal) { auto kind = getFragileFunctionKind(DC); diagnose(NTD, diag::local_type_in_inlinable_function, NTD->getFullName(), static_cast(kind.first)); } } /// A uniquely-typed boolean to reduce the chances of accidentally inverting /// a check. enum class DowngradeToWarning: bool { No, Yes }; bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D, const DeclContext *DC, FragileFunctionKind Kind, bool TreatUsableFromInlineAsPublic) { // Local declarations are OK. if (D->getDeclContext()->isLocalContext()) return false; // Type parameters are OK. if (isa(D)) return false; // Public declarations are OK. if (D->getFormalAccessScope(/*useDC=*/nullptr, TreatUsableFromInlineAsPublic).isPublic()) return false; // Enum cases are handled as part of their containing enum. if (isa(D)) return false; // Protocol requirements are not versioned because there's no // global entry point. if (isa(D->getDeclContext()) && D->isProtocolRequirement()) return false; // Dynamic declarations are not versioned because there's no // global entry point. if (D->isDynamic()) return false; DowngradeToWarning downgradeToWarning = DowngradeToWarning::No; // Swift 4.2 did not perform any checks for type aliases. if (isa(D)) { if (!Context.isSwiftVersionAtLeast(4, 2)) return false; if (!Context.isSwiftVersionAtLeast(5)) downgradeToWarning = DowngradeToWarning::Yes; } auto diagID = diag::resilience_decl_unavailable; if (downgradeToWarning == DowngradeToWarning::Yes) diagID = diag::resilience_decl_unavailable_warn; diagnose(loc, diagID, D->getDescriptiveKind(), D->getFullName(), D->getFormalAccessScope().accessLevelForDiagnostics(), static_cast(Kind)); if (TreatUsableFromInlineAsPublic) { diagnose(D, diag::resilience_decl_declared_here, D->getDescriptiveKind(), D->getFullName()); } else { diagnose(D, diag::resilience_decl_declared_here_public, D->getDescriptiveKind(), D->getFullName()); } return (downgradeToWarning == DowngradeToWarning::No); }