Skip to content

Fold exportability checking into availability checking #34329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 68 additions & 99 deletions lib/Sema/ResilienceDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,53 +27,47 @@

using namespace swift;

/// A uniquely-typed boolean to reduce the chances of accidentally inverting
/// a check.
enum class DowngradeToWarning: bool {
No,
Yes
};

bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc,
ConcreteDeclRef declRef,
const DeclContext *DC,
FragileFunctionKind Kind) {
assert(Kind.kind != FragileFunctionKind::None);
const ValueDecl *D,
ExportContext where) {
auto fragileKind = where.getFragileFunctionKind();
if (fragileKind.kind == FragileFunctionKind::None)
return false;

const ValueDecl *D = declRef.getDecl();
// Do some important fast-path checks that apply to all cases.

// Type parameters are OK.
if (isa<AbstractTypeParamDecl>(D))
return false;

// Check whether the declaration is accessible.
if (diagnoseInlinableDeclRefAccess(loc, D, DC, Kind))
if (diagnoseInlinableDeclRefAccess(loc, D, where))
return true;

// Check whether the declaration comes from a publically-imported module.
// Skip this check for accessors because the associated property or subscript
// will also be checked, and will provide a better error message.
if (!isa<AccessorDecl>(D))
if (diagnoseDeclRefExportability(loc, declRef, DC, Kind))
if (diagnoseDeclRefExportability(loc, D, where))
return true;

return false;
}

bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
const ValueDecl *D,
const DeclContext *DC,
FragileFunctionKind Kind) {
assert(Kind.kind != FragileFunctionKind::None);
const ValueDecl *D,
ExportContext where) {
auto *DC = where.getDeclContext();
auto fragileKind = where.getFragileFunctionKind();
assert(fragileKind.kind != FragileFunctionKind::None);

// Local declarations are OK.
if (D->getDeclContext()->isLocalContext())
return false;

// Public declarations or SPI used from SPI are OK.
if (D->getFormalAccessScope(/*useDC=*/nullptr,
Kind.allowUsableFromInline).isPublic() &&
fragileKind.allowUsableFromInline).isPublic() &&
!(D->isSPI() && !DC->getInnermostDeclarationDeclContext()->isSPI()))
return false;

Expand Down Expand Up @@ -133,10 +127,10 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
loc, diagID,
D->getDescriptiveKind(), diagName,
D->getFormalAccessScope().accessLevelForDiagnostics(),
static_cast<unsigned>(Kind.kind),
static_cast<unsigned>(fragileKind.kind),
isAccessor);

if (Kind.allowUsableFromInline) {
if (fragileKind.allowUsableFromInline) {
Context.Diags.diagnose(D, diag::resilience_decl_declared_here,
D->getDescriptiveKind(), diagName, isAccessor);
} else {
Expand All @@ -147,99 +141,74 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
return (downgradeToWarning == DowngradeToWarning::No);
}

static bool diagnoseDeclExportability(SourceLoc loc, const ValueDecl *D,
const SourceFile &userSF,
const DeclContext *userDC,
FragileFunctionKind fragileKind) {
assert(fragileKind.kind != FragileFunctionKind::None);
bool
TypeChecker::diagnoseDeclRefExportability(SourceLoc loc,
const ValueDecl *D,
ExportContext where) {
if (!where.mustOnlyReferenceExportedDecls())
return false;

auto definingModule = D->getModuleContext();

auto downgradeToWarning = DowngradeToWarning::No;

auto originKind = getDisallowedOriginKind(
D, userSF, userDC->getInnermostDeclarationDeclContext());
D, where, downgradeToWarning);
if (originKind == DisallowedOriginKind::None)
return false;

// TODO: different diagnostics
ASTContext &ctx = definingModule->getASTContext();
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;
}

static bool
diagnoseGenericArgumentsExportability(SourceLoc loc,
SubstitutionMap subs,
const SourceFile &userSF,
const DeclContext *userDC) {
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, userDC);

const RootProtocolConformance *rootConf =
concreteConf->getRootConformance();
ModuleDecl *M = rootConf->getDeclContext()->getParentModule();

auto originKind = getDisallowedOriginKind(
rootConf->getDeclContext()->getAsDecl(),
userSF, userDC->getInnermostDeclarationDeclContext());
if (originKind == DisallowedOriginKind::None)
continue;

ASTContext &ctx = M->getASTContext();
ctx.Diags.diagnose(loc, diag::conformance_from_implementation_only_module,
rootConf->getType(),
rootConf->getProtocol()->getName(), 0, M->getName(),
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));
hadAnyIssues = true;
}
return hadAnyIssues;
}

void TypeChecker::diagnoseGenericTypeExportability(SourceLoc Loc, Type T,
const DeclContext *DC) {
const SourceFile *SF = DC->getParentSourceFile();
if (!SF)
return;

// 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.
if (auto *BGT = dyn_cast<BoundGenericType>(T.getPointer())) {
ModuleDecl *useModule = SF->getParentModule();
auto subs = T->getContextSubstitutionMap(useModule, BGT->getDecl());
(void)diagnoseGenericArgumentsExportability(Loc, subs, *SF, DC);
} else if (auto *TAT = dyn_cast<TypeAliasType>(T.getPointer())) {
auto subs = TAT->getSubstitutionMap();
(void)diagnoseGenericArgumentsExportability(Loc, subs, *SF, DC);
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::diagnoseDeclRefExportability(SourceLoc loc,
ConcreteDeclRef declRef,
const DeclContext *DC,
FragileFunctionKind fragileKind) {
// We're only interested in diagnosing uses from source files.
auto userSF = DC->getParentSourceFile();
if (!userSF)
TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
const RootProtocolConformance *rootConf,
ExportContext where) {
if (!where.mustOnlyReferenceExportedDecls())
return false;

const ValueDecl *D = declRef.getDecl();
if (diagnoseDeclExportability(loc, D, *userSF, DC, fragileKind))
return true;
if (diagnoseGenericArgumentsExportability(loc, declRef.getSubstitutions(),
*userSF, DC)) {
return true;
}
return false;
auto originKind = getDisallowedOriginKind(
rootConf->getDeclContext()->getAsDecl(),
where);
if (originKind == DisallowedOriginKind::None)
return false;

ModuleDecl *M = rootConf->getDeclContext()->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;
}
Loading