Files
swift-mirror/lib/Sema/ResilienceDiagnostics.cpp
Slava Pestov ceb8675ad1 Sema: (Mostly) check conformance availability
If a conformance is defined in an extension, we now look for
references to the conformance in types and expressions and
respect's the extension's availability (or deprecation, etc).

The conformance checker itself still needs to check conformance
availability of associated conformances and the like; that will
be a separate change.

Note that conformances defined on types don't require any
special handling, since they are as available as the
intersection of the conforming type and the protocol.

By default, we diagnose conformance availability violations
where the OS version is not sufficiently new as warnings, to
avoid breaking source compatibility. Stricter behavior where
these violations are diagnosed as errors is enabled by passing
the -enable-conformance-availability-errors flag. There are
test cases that run both with and without this flag. In the
future, we hope to make the stricter behavior the default,
since after all, violations here can result in link errors and
runtime crashes.

Uses of completely unavailable conformances are still always
diagnosed as errors, even when this flag is not passed in.

Progress on <rdar://problem/35158274>.
2020-11-05 17:51:45 -05:00

187 lines
6.6 KiB
C++

//===--- 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 "TypeCheckAvailability.h"
#include "TypeCheckAccess.h"
#include "swift/AST/Attr.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DeclContext.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeDeclFinder.h"
using namespace swift;
bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
const ValueDecl *D,
const ExportContext &where) {
auto fragileKind = where.getFragileFunctionKind();
if (fragileKind.kind == FragileFunctionKind::None)
return false;
// Local declarations are OK.
if (D->getDeclContext()->isLocalContext())
return false;
auto *DC = where.getDeclContext();
// Public declarations are OK, even if they're SPI or came from an
// implementation-only import. We'll diagnose exportability violations
// from diagnoseDeclRefExportability().
if (D->getFormalAccessScope(/*useDC=*/nullptr,
fragileKind.allowUsableFromInline).isPublic())
return false;
auto &Context = DC->getASTContext();
// Dynamic declarations were mistakenly not checked in Swift 4.2.
// Do enforce the restriction even in pre-Swift-5 modes if the module we're
// building is resilient, though.
if (D->shouldUseObjCDispatch() && !Context.isSwiftVersionAtLeast(5) &&
!DC->getParentModule()->isResilient()) {
return false;
}
DowngradeToWarning downgradeToWarning = DowngradeToWarning::No;
// Swift 4.2 did not perform any checks for type aliases.
if (isa<TypeAliasDecl>(D)) {
if (!Context.isSwiftVersionAtLeast(4, 2))
return false;
if (!Context.isSwiftVersionAtLeast(5))
downgradeToWarning = DowngradeToWarning::Yes;
}
auto diagName = D->getName();
bool isAccessor = false;
// Swift 4.2 did not check accessor accessiblity.
if (auto accessor = dyn_cast<AccessorDecl>(D)) {
isAccessor = true;
if (!Context.isSwiftVersionAtLeast(5))
downgradeToWarning = DowngradeToWarning::Yes;
// For accessors, diagnose with the name of the storage instead of the
// implicit '_'.
diagName = accessor->getStorage()->getName();
}
// Swift 5.0 did not check the underlying types of local typealiases.
// FIXME: Conditionalize this once we have a new language mode.
if (isa<TypeAliasDecl>(DC))
downgradeToWarning = DowngradeToWarning::Yes;
auto diagID = diag::resilience_decl_unavailable;
if (downgradeToWarning == DowngradeToWarning::Yes)
diagID = diag::resilience_decl_unavailable_warn;
Context.Diags.diagnose(
loc, diagID,
D->getDescriptiveKind(), diagName,
D->getFormalAccessScope().accessLevelForDiagnostics(),
static_cast<unsigned>(fragileKind.kind),
isAccessor);
if (fragileKind.allowUsableFromInline) {
Context.Diags.diagnose(D, diag::resilience_decl_declared_here,
D->getDescriptiveKind(), diagName, isAccessor);
} else {
Context.Diags.diagnose(D, diag::resilience_decl_declared_here_public,
D->getDescriptiveKind(), diagName, isAccessor);
}
return (downgradeToWarning == DowngradeToWarning::No);
}
bool
TypeChecker::diagnoseDeclRefExportability(SourceLoc loc,
const ValueDecl *D,
const ExportContext &where) {
// Accessors cannot have exportability that's different than the storage,
// so skip them for now.
if (isa<AccessorDecl>(D))
return false;
if (!where.mustOnlyReferenceExportedDecls())
return false;
auto definingModule = D->getModuleContext();
auto downgradeToWarning = DowngradeToWarning::No;
auto originKind = getDisallowedOriginKind(
D, where, downgradeToWarning);
if (originKind == DisallowedOriginKind::None)
return false;
ASTContext &ctx = definingModule->getASTContext();
auto fragileKind = where.getFragileFunctionKind();
auto reason = where.getExportabilityReason();
if (fragileKind.kind == FragileFunctionKind::None) {
auto errorOrWarning = downgradeToWarning == DowngradeToWarning::Yes?
diag::decl_from_hidden_module_warn:
diag::decl_from_hidden_module;
ctx.Diags.diagnose(loc, errorOrWarning,
D->getDescriptiveKind(),
D->getName(),
static_cast<unsigned>(*reason),
definingModule->getName(),
static_cast<unsigned>(originKind));
D->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);
} else {
ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_from_hidden_module,
D->getDescriptiveKind(), D->getName(),
static_cast<unsigned>(fragileKind.kind),
definingModule->getName(),
static_cast<unsigned>(originKind));
}
return true;
}
bool
TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
const ExportContext &where) {
if (!where.mustOnlyReferenceExportedDecls())
return false;
auto originKind = getDisallowedOriginKind(ext, where);
if (originKind == DisallowedOriginKind::None)
return false;
ModuleDecl *M = ext->getParentModule();
ASTContext &ctx = M->getASTContext();
auto reason = where.getExportabilityReason();
if (!reason.hasValue())
reason = ExportabilityReason::General;
ctx.Diags.diagnose(loc, diag::conformance_from_implementation_only_module,
rootConf->getType(),
rootConf->getProtocol()->getName(),
static_cast<unsigned>(*reason),
M->getName(),
static_cast<unsigned>(originKind));
return true;
}