Skip to content

Commit 937e6c5

Browse files
authored
Merge pull request #85446 from xymus/serial-xref-check
Serialization: Error on leaked cross-references to `@_implementationOnly` dependencies
2 parents dc7f30f + 5a49e34 commit 937e6c5

File tree

14 files changed

+246
-35
lines changed

14 files changed

+246
-35
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,10 @@ REMARK(serialization_skipped_invalid_type_unknown_name,none,
887887
ERROR(serialization_failed,none,
888888
"serialization of module %0 failed due to the errors above",
889889
(const ModuleDecl *))
890+
ERROR(serialization_xref_to_hidden_dependency,none,
891+
"invalid reference to implementation-only imported module %0"
892+
"%select{| for %1}1",
893+
(const ModuleDecl *, const Decl *))
890894

891895
WARNING(can_import_invalid_swiftmodule,none,
892896
"canImport() evaluated to false due to invalid swiftmodule: %0", (StringRef))

include/swift/AST/Module.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,11 +1064,15 @@ class ModuleDecl
10641064
void
10651065
getImportedModulesForLookup(SmallVectorImpl<ImportedModule> &imports) const;
10661066

1067-
/// Has \p module been imported via an '@_implementationOnly' import
1068-
/// instead of another kind of import?
1067+
/// Has \p module been imported via an '@_implementationOnly' import and
1068+
/// not by anything more visible?
10691069
///
1070-
/// This assumes that \p module was imported.
1071-
bool isImportedImplementationOnly(const ModuleDecl *module) const;
1070+
/// If \p assumeImported, assume that \p module was imported and avoid the
1071+
/// work to confirm it is imported at all. Transitive modules not reexported
1072+
/// are not considered imported here and may lead to false positive without
1073+
/// this setting.
1074+
bool isImportedImplementationOnly(const ModuleDecl *module,
1075+
bool assumeImported = true) const;
10721076

10731077
/// Finds all top-level decls of this module.
10741078
///

include/swift/Basic/Features.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,10 @@ EXPERIMENTAL_FEATURE(AnyAppleOSAvailability, true)
557557
/// Check @_implementationOnly imports in non-library-evolution mode.
558558
EXPERIMENTAL_FEATURE(CheckImplementationOnly, true)
559559

560+
/// Extends CheckImplementationOnly with an extra check at serialization to
561+
/// report references to implementation-only imported modules.
562+
EXPERIMENTAL_FEATURE(CheckImplementationOnlyStrict, false)
563+
560564
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
561565
#undef EXPERIMENTAL_FEATURE
562566
#undef UPCOMING_FEATURE

include/swift/Serialization/SerializationOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ class SerializationOptions {
163163
bool HermeticSealAtLink = false;
164164
bool EmbeddedSwiftModule = false;
165165
bool SkipNonExportableDecls = false;
166+
bool SkipImplementationOnlyDecls = false;
166167
bool ExplicitModuleBuild = false;
167168
bool EnableSerializationRemarks = false;
168169
bool IsInterfaceSDKRelative = false;

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ UNINTERESTING_FEATURE(GroupActorErrors)
145145
UNINTERESTING_FEATURE(SameElementRequirements)
146146
UNINTERESTING_FEATURE(SendingArgsAndResults)
147147
UNINTERESTING_FEATURE(CheckImplementationOnly)
148+
UNINTERESTING_FEATURE(CheckImplementationOnlyStrict)
148149

149150
static bool findUnderscoredLifetimeAttr(Decl *decl) {
150151
auto hasUnderscoredLifetimeAttr = [](Decl *decl) {

lib/AST/Module.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3078,7 +3078,8 @@ void ModuleDecl::setPackageName(Identifier name) {
30783078
Package = PackageUnit::create(name, *this, getASTContext());
30793079
}
30803080

3081-
bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
3081+
bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module,
3082+
bool assumeImported) const {
30823083
if (module == this) return false;
30833084

30843085
auto &imports = getASTContext().getImportCache();
@@ -3099,7 +3100,17 @@ bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
30993100
return false;
31003101
}
31013102

3102-
return true;
3103+
if (assumeImported)
3104+
return true;
3105+
3106+
results.clear();
3107+
getImportedModules(results,
3108+
{ModuleDecl::ImportFilterKind::ImplementationOnly});
3109+
for (auto &desc : results)
3110+
if (imports.isImportedBy(module, desc.importedModule))
3111+
return true;
3112+
3113+
return false;
31033114
}
31043115

31053116
void SourceFile::lookupImportedSPIGroups(

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,6 +1897,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
18971897
Opts.enableFeature(Feature::NoExplicitNonIsolated);
18981898
}
18991899

1900+
if (Opts.hasFeature(Feature::CheckImplementationOnlyStrict) &&
1901+
!::getenv("SWIFT_DISABLE_IMPLICIT_CHECK_IMPLEMENTATION_ONLY"))
1902+
Opts.enableFeature(Feature::CheckImplementationOnly);
1903+
19001904
#if !defined(NDEBUG) && SWIFT_ENABLE_EXPERIMENTAL_PARSER_VALIDATION
19011905
/// Enable round trip parsing via the new swift parser unless it is disabled
19021906
/// explicitly. The new Swift parser can have mismatches with C++ parser -

lib/Frontend/Frontend.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,9 @@ SerializationOptions CompilerInvocation::computeSerializationOptions(
284284
serializationOpts.SkipNonExportableDecls =
285285
getLangOptions().SkipNonExportableDecls;
286286

287+
serializationOpts.SkipImplementationOnlyDecls =
288+
getLangOptions().hasFeature(Feature::CheckImplementationOnly);
289+
287290
serializationOpts.ExplicitModuleBuild = FrontendOpts.DisableImplicitModules;
288291

289292
serializationOpts.EnableSerializationRemarks =

lib/Sema/TypeCheckAttr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5051,7 +5051,8 @@ AttributeChecker::visitImplementationOnlyAttr(ImplementationOnlyAttr *attr) {
50515051

50525052
// @_implementationOnly on types only applies to non-public types.
50535053
if (isa<NominalTypeDecl>(D)) {
5054-
if (!Ctx.LangOpts.hasFeature(Feature::CheckImplementationOnly)) {
5054+
if (!Ctx.LangOpts.hasFeature(Feature::CheckImplementationOnly) &&
5055+
!Ctx.LangOpts.hasFeature(Feature::CheckImplementationOnlyStrict)) {
50555056
diagnoseAndRemoveAttr(attr, diag::implementation_only_on_types_feature);
50565057
return;
50575058
}

lib/Serialization/Serialization.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,16 @@ IdentifierID Serializer::addContainingModuleRef(const DeclContext *DC,
756756
if (M->isClangHeaderImportModule())
757757
return OBJC_HEADER_MODULE_ID;
758758

759+
// Reject references to hidden dependencies.
760+
if (getASTContext().LangOpts.hasFeature(
761+
Feature::CheckImplementationOnlyStrict) &&
762+
!allowCompilerErrors() &&
763+
this->M->isImportedImplementationOnly(M, /*assumeImported=*/false)) {
764+
getASTContext().Diags.diagnose(SourceLoc(),
765+
diag::serialization_xref_to_hidden_dependency,
766+
M, crossReferencedDecl);
767+
}
768+
759769
auto exportedModuleName = file->getExportedModuleName();
760770
assert(!exportedModuleName.empty());
761771
auto moduleID = M->getASTContext().getIdentifier(exportedModuleName);
@@ -2452,6 +2462,8 @@ void Serializer::writeCrossReference(const Decl *D) {
24522462

24532463
unsigned abbrCode;
24542464

2465+
llvm::SaveAndRestore<const Decl *> SaveDecl(crossReferencedDecl, D);
2466+
24552467
if (auto op = dyn_cast<OperatorDecl>(D)) {
24562468
writeCrossReference(op->getDeclContext(), 1);
24572469

@@ -5389,6 +5401,19 @@ bool Serializer::shouldSkipDecl(const Decl *D) const {
53895401
void Serializer::writeASTBlockEntity(const Decl *D) {
53905402
using namespace decls_block;
53915403

5404+
if (Options.SkipImplementationOnlyDecls) {
5405+
// Skip @_implementationOnly types.
5406+
if (D->getAttrs().hasAttribute<ImplementationOnlyAttr>())
5407+
return;
5408+
5409+
// Skip non-public @export(interface) functions.
5410+
auto FD = dyn_cast<AbstractFunctionDecl>(D);
5411+
if (FD && FD->isNeverEmittedIntoClient() &&
5412+
!FD->getFormalAccessScope(/*useDC*/nullptr,
5413+
/*treatUsableFromInlineAsPublic*/true).isPublicOrPackage())
5414+
return;
5415+
}
5416+
53925417
PrettyStackTraceDecl trace("serializing", D);
53935418
assert(DeclsToSerialize.hasRef(D));
53945419

@@ -7301,7 +7326,7 @@ void Serializer::writeToStream(
73017326
BCBlockRAII moduleBlock(S.Out, MODULE_BLOCK_ID, 2);
73027327
S.writeHeader();
73037328
S.writeInputBlock();
7304-
S.writeSIL(SILMod, options.SerializeAllSIL, options.SerializeDebugInfoSIL);
7329+
S.writeSIL(SILMod);
73057330
S.writeAST(DC);
73067331

73077332
if (S.hadError)

0 commit comments

Comments
 (0)