Skip to content

[Sema] Handle package exportability. #73161

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 2 commits into from
May 3, 2024
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
40 changes: 24 additions & 16 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2286,32 +2286,32 @@ ERROR(pattern_type_not_usable_from_inline,none,
"type referenced from a '@usableFromInline' "
"%select{%select{variable|constant}0|property}1 "
"must be '@usableFromInline' or public",
(bool, bool))
(bool, bool, /*ignored*/bool))
WARNING(pattern_type_not_usable_from_inline_warn,none,
"type referenced from a '@usableFromInline' "
"%select{%select{variable|constant}0|property}1 "
"should be '@usableFromInline' or public",
(bool, bool))
(bool, bool, /*ignored*/bool))
ERROR(pattern_type_not_usable_from_inline_frozen,none,
"type referenced from a stored property in a '@frozen' struct must "
"be '@usableFromInline' or public",
(/*ignored*/bool, /*ignored*/bool))
"type referenced from a stored property in a '@frozen%select{| package}2' struct must "
"be '@usableFromInline'%select{ or public|, public, or package}2",
(/*ignored*/bool, /*ignored*/bool, bool))
ERROR(pattern_type_not_usable_from_inline_inferred,none,
"type referenced from a '@usableFromInline' "
"%select{%select{variable|constant}0|property}1 "
"with inferred type %2 "
"must be '@usableFromInline' or public",
(bool, bool, Type))
(bool, bool, Type, /*ignored*/bool))
WARNING(pattern_type_not_usable_from_inline_inferred_warn,none,
"type referenced from a '@usableFromInline' "
"%select{%select{variable|constant}0|property}1 "
"with inferred type %2 "
"should be '@usableFromInline' or public",
(bool, bool, Type))
(bool, bool, Type, /*ignored*/bool))
ERROR(pattern_type_not_usable_from_inline_inferred_frozen,none,
"type referenced from a stored property with inferred type %2 in a "
"'@frozen' struct must be '@usableFromInline' or public",
(/*ignored*/bool, /*ignored*/bool, Type))
"'@frozen%select{| package}3' struct must be '@usableFromInline'%select{ or public|, public, or package}3",
(/*ignored*/bool, /*ignored*/bool, Type, bool))

ERROR(pattern_binds_no_variables,none,
"%select{property|global variable}0 declaration does not bind any "
Expand Down Expand Up @@ -3626,14 +3626,18 @@ ERROR(decl_from_hidden_module,none,
"cannot use %kind0 %select{here|as property wrapper here|"
"as result builder here|"
"in an extension with public or '@usableFromInline' members|"
"in an extension with conditional conformances|"
"in an extension with public, package, or '@usableFromInline' members|"
"in an extension with conditional conformances}1; "
"%select{%2 has been imported as implementation-only|"
"it is an SPI imported from %2|"
"it is SPI|"
"%2 was imported for SPI only|"
"%2 was not imported by this file|"
"C++ types from imported module %2 do not support library evolution|"
"%2 was not imported publicly}3",
"%2 was not imported publicly|"
"%2 was imported as package}3"
"%select{||||| or as package| or as package}1",
(const Decl *, unsigned, Identifier, unsigned))
ERROR(typealias_desugars_to_type_from_hidden_module,none,
"%0 aliases '%1.%2' and cannot be used %select{here|"
Expand All @@ -3647,7 +3651,8 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
"%4 was imported for SPI only|"
"%4 was not imported by this file|"
"C++ types from imported module %4 do not support library evolution|"
"%4 was not imported publicly}5",
"%4 was not imported publicly|"
"%4 was imported as package}5",
(const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned))
ERROR(conformance_from_implementation_only_module,none,
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
Expand All @@ -3660,7 +3665,8 @@ ERROR(conformance_from_implementation_only_module,none,
"%3 was imported for SPI only|"
"%3 was not imported by this file|"
"C++ types from imported module %3 do not support library evolution|"
"%3 was not imported publicly}4",
"%3 was not imported publicly|"
"%3 was imported as package}4",
(Type, Identifier, unsigned, Identifier, unsigned))
NOTE(assoc_conformance_from_implementation_only_module,none,
"in associated type %0 (inferred as %1)", (Type, Type))
Expand Down Expand Up @@ -6884,7 +6890,8 @@ ERROR(inlinable_decl_ref_from_hidden_module,
"%2 was imported for SPI only|"
"%2 was not imported by this file|"
"C++ APIs from imported module %2 do not support library evolution|"
"%2 was not imported publicly}3",
"%2 was not imported publicly|"
"%2 was imported as package}3",
(const ValueDecl *, unsigned, Identifier, unsigned))

WARNING(inlinable_decl_ref_from_hidden_module_warn,
Expand All @@ -6902,7 +6909,8 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
"%4 was imported for SPI only|"
"%4 was not imported by this file|"
"C++ types from imported module %4 do not support library evolution|"
"%4 was not imported publicly}5",
"%4 was not imported publicly|"
"%4 was imported as package}5",
(const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned))

NOTE(missing_import_inserted,
Expand All @@ -6916,8 +6924,8 @@ ERROR(availability_macro_in_inlinable, none,
#undef FRAGILE_FUNC_KIND

NOTE(resilience_decl_declared_here,
none, "%kind0 is not '@usableFromInline' or public",
(const ValueDecl *))
none, "%kind0 is not '@usableFromInline'%select{ or public|, public, or package}1",
(const ValueDecl *, bool))

ERROR(class_designated_init_inlinable_resilient,none,
"initializer for class %0 is "
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/SourceFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,10 @@ class SourceFile final : public FileUnit {
void setImportUsedPreconcurrency(
AttributedImport<ImportedModule> import);

/// True if the highest access level of the declarations referencing
/// this import in signature or inlinable code is internal or less.
bool isMaxAccessLevelUsingImportInternal(AttributedImport<ImportedModule> import) const;

/// Return the highest access level of the declarations referencing
/// this import in signature or inlinable code.
AccessLevel
Expand Down
6 changes: 4 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2324,7 +2324,7 @@ bool VarDecl::isLayoutExposedToClients() const {
auto nominalAccess =
parent->getFormalAccessScope(/*useDC=*/nullptr,
/*treatUsableFromInlineAsPublic=*/true);
if (!nominalAccess.isPublic()) return false;
if (!nominalAccess.isPublicOrPackage()) return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: move return false to a new line for better debuggability.


if (!parent->getAttrs().hasAttribute<FrozenAttr>() &&
!parent->getAttrs().hasAttribute<FixedLayoutAttr>())
Expand Down Expand Up @@ -4571,8 +4571,10 @@ bool ValueDecl::isMoreVisibleThan(ValueDecl *other) const {

if (scope.isPublic())
return !otherScope.isPublic();
else if (scope.isPackage())
return !otherScope.isPublicOrPackage();
else if (scope.isInternal())
return !otherScope.isPublic() && !otherScope.isInternal();
return !otherScope.isPublicOrPackage() && !otherScope.isInternal();
else
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
auto effectiveAccess =
VD->getFormalAccessScope(/*useDC=*/nullptr,
/*treatUsableFromInlineAsPublic=*/true);
if (effectiveAccess.isPublic()) {
if (effectiveAccess.isPublicOrPackage()) {
return {FragileFunctionKind::DefaultArgument};
}

Expand Down Expand Up @@ -499,7 +499,7 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,

// If the function is not externally visible, we will not be serializing
// its body.
if (!funcAccess.isPublic()) {
if (!funcAccess.isPublicOrPackage()) {
return {FragileFunctionKind::None};
}

Expand Down
6 changes: 6 additions & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2623,6 +2623,12 @@ void SourceFile::setImportUsedPreconcurrency(
PreconcurrencyImportsUsed.insert(import);
}

bool SourceFile::isMaxAccessLevelUsingImportInternal(
AttributedImport<ImportedModule> import) const {
auto maxLevel = getMaxAccessLevelUsingImport(import.module.importedModule);
return maxLevel < AccessLevel::Package;
}

AccessLevel
SourceFile::getMaxAccessLevelUsingImport(
const ModuleDecl *mod) const {
Expand Down
125 changes: 86 additions & 39 deletions lib/Sema/ResilienceDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,22 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
ImportAccessLevel problematicImport = D->getImportAccessFrom(DC);
if (problematicImport.has_value()) {
auto SF = DC->getParentSourceFile();
if (SF)
SF->registerAccessLevelUsingImport(problematicImport.value(),
AccessLevel::Public);

if (Context.LangOpts.EnableModuleApiImportRemarks) {
ModuleDecl *importedVia = problematicImport->module.importedModule,
*sourceModule = D->getModuleContext();
Context.Diags.diagnose(loc, diag::module_api_import,
D, importedVia, sourceModule,
importedVia == sourceModule,
/*isImplicit*/false);
if (SF) {
// The max used access level previously registered might be Package,
// in which case, don't reset it to Public here; this ensures proper
// diags between public and package.
if (SF->isMaxAccessLevelUsingImportInternal(problematicImport.value()))
SF->registerAccessLevelUsingImport(problematicImport.value(),
AccessLevel::Public);

if (Context.LangOpts.EnableModuleApiImportRemarks) {
ModuleDecl *importedVia = problematicImport->module.importedModule,
*sourceModule = D->getModuleContext();
Context.Diags.diagnose(loc, diag::module_api_import,
D, importedVia, sourceModule,
importedVia == sourceModule,
/*isImplicit*/false);
}
}
}

Expand All @@ -100,6 +105,16 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
}

DowngradeToWarning downgradeToWarning = DowngradeToWarning::No;
// Don't change the order of the getDisallowedOriginKind call;
// it can reset downgradeToWarning to NO so needs to be called here.
auto originKind = getDisallowedOriginKind(D, where, downgradeToWarning);
// For a default argument or property initializer, package type is
// allowed at the use site with package access scope.
auto allowedForPkgCtx = false;
if (originKind == DisallowedOriginKind::None ||
originKind == DisallowedOriginKind::PackageImport) {
allowedForPkgCtx = where.isPackage() && declAccessScope.isPackage();
}

// Swift 4.2 did not perform any checks for type aliases.
if (isa<TypeAliasDecl>(D)) {
Expand All @@ -119,15 +134,17 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
if (isa<TypeAliasDecl>(DC) && !Context.isSwiftVersionAtLeast(6))
downgradeToWarning = DowngradeToWarning::Yes;

auto diagID = diag::resilience_decl_unavailable;
if (downgradeToWarning == DowngradeToWarning::Yes)
diagID = diag::resilience_decl_unavailable_warn;
if (!allowedForPkgCtx) {
auto diagID = diag::resilience_decl_unavailable;
if (downgradeToWarning == DowngradeToWarning::Yes)
diagID = diag::resilience_decl_unavailable_warn;

AccessLevel diagAccessLevel = declAccessScope.accessLevelForDiagnostics();
Context.Diags.diagnose(loc, diagID, D, diagAccessLevel,
fragileKind.getSelector());
AccessLevel diagAccessLevel = declAccessScope.accessLevelForDiagnostics();
Context.Diags.diagnose(loc, diagID, D, diagAccessLevel,
fragileKind.getSelector());

Context.Diags.diagnose(D, diag::resilience_decl_declared_here, D);
Context.Diags.diagnose(D, diag::resilience_decl_declared_here, D, allowedForPkgCtx);
}

if (problematicImport.has_value() &&
problematicImport->accessLevel < D->getFormalAccess()) {
Expand Down Expand Up @@ -156,10 +173,14 @@ static bool diagnoseTypeAliasDeclRefExportability(SourceLoc loc,
where.getDeclContext());
if (problematicImport.has_value()) {
auto SF = where.getDeclContext()->getParentSourceFile();
if (SF)
SF->registerAccessLevelUsingImport(problematicImport.value(),
AccessLevel::Public);

if (SF) {
// The max used access level previously registered might be Package,
// in which case, don't reset it to Public here; this ensures proper
// diags between public and package.
if (SF->isMaxAccessLevelUsingImportInternal(problematicImport.value()))
SF->registerAccessLevelUsingImport(problematicImport.value(),
AccessLevel::Public);
}
if (ctx.LangOpts.EnableModuleApiImportRemarks) {
ModuleDecl *importedVia = problematicImport->module.importedModule,
*sourceModule = D->getModuleContext();
Expand All @@ -186,7 +207,8 @@ static bool diagnoseTypeAliasDeclRefExportability(SourceLoc loc,
auto definingModule = D->getModuleContext();
auto fragileKind = where.getFragileFunctionKind();
bool warnPreSwift6 = originKind != DisallowedOriginKind::SPIOnly &&
originKind != DisallowedOriginKind::NonPublicImport;
originKind != DisallowedOriginKind::PackageImport &&
originKind != DisallowedOriginKind::InternalOrLessImport;
if (fragileKind.kind == FragileFunctionKind::None) {
auto reason = where.getExportabilityReason();
ctx.Diags
Expand All @@ -211,7 +233,8 @@ static bool diagnoseTypeAliasDeclRefExportability(SourceLoc loc,
addMissingImport(loc, D, where);

// If limited by an import, note which one.
if (originKind == DisallowedOriginKind::NonPublicImport) {
if (originKind == DisallowedOriginKind::InternalOrLessImport ||
originKind == DisallowedOriginKind::PackageImport) {
const DeclContext *DC = where.getDeclContext();
ImportAccessLevel limitImport = D->getImportAccessFrom(DC);
assert(limitImport.has_value() &&
Expand Down Expand Up @@ -242,22 +265,31 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
ImportAccessLevel import = D->getImportAccessFrom(DC);
if (import.has_value() && reason.has_value()) {
auto SF = DC->getParentSourceFile();
if (SF)
SF->registerAccessLevelUsingImport(import.value(),
AccessLevel::Public);
if (SF) {
// The max used access level previously registered might be Package,
// in which case, don't reset it to Public here; this ensures proper
// diags between public and package.
if (SF->isMaxAccessLevelUsingImportInternal(import.value()))
SF->registerAccessLevelUsingImport(import.value(),
AccessLevel::Public);
}
}

// Access levels from imports are reported with the others access levels.
// Except for extensions, we report them here.
if (originKind == DisallowedOriginKind::NonPublicImport &&
reason != ExportabilityReason::ExtensionWithPublicMembers &&
reason != ExportabilityReason::ExtensionWithConditionalConformances)
return false;
if (originKind == DisallowedOriginKind::InternalOrLessImport ||
originKind == DisallowedOriginKind::PackageImport) {
if (reason != ExportabilityReason::ExtensionWithPublicMembers &&
reason != ExportabilityReason::ExtensionWithPackageMembers &&
reason != ExportabilityReason::ExtensionWithConditionalConformances &&
reason != ExportabilityReason::ExtensionWithPackageConditionalConformances)
return false;
}

if (ctx.LangOpts.EnableModuleApiImportRemarks &&
import.has_value() && where.isExported() &&
reason != ExportabilityReason::General &&
originKind != DisallowedOriginKind::NonPublicImport) {
originKind != DisallowedOriginKind::InternalOrLessImport) {
// These may be reported twice, for the Type and for the TypeRepr.
ModuleDecl *importedVia = import->module.importedModule,
*sourceModule = D->getModuleContext();
Expand All @@ -270,6 +302,14 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
if (originKind == DisallowedOriginKind::None)
return false;

// No diags needed for extensions with package members or
// conformance to types with package access scope.
if (originKind == DisallowedOriginKind::PackageImport) {
if (reason == ExportabilityReason::ExtensionWithPackageMembers ||
reason == ExportabilityReason::ExtensionWithPackageConditionalConformances)
return false;
}

auto diagName = D->getName();
if (auto accessor = dyn_cast<AccessorDecl>(D)) {
// Only diagnose accessors if their disallowed origin kind differs from
Expand Down Expand Up @@ -313,7 +353,8 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
}

// If limited by an import, note which one.
if (originKind == DisallowedOriginKind::NonPublicImport) {
if (originKind == DisallowedOriginKind::InternalOrLessImport ||
originKind == DisallowedOriginKind::PackageImport) {
assert(import.has_value() &&
import->accessLevel < AccessLevel::Public &&
"The import should still be non-public");
Expand Down Expand Up @@ -362,10 +403,14 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
ImportAccessLevel problematicImport = ext->getImportAccessFrom(where.getDeclContext());
if (problematicImport.has_value()) {
auto SF = where.getDeclContext()->getParentSourceFile();
if (SF)
SF->registerAccessLevelUsingImport(problematicImport.value(),
AccessLevel::Public);

if (SF) {
// The max used access level previously registered might be Package,
// in which case, don't reset it to Public here; this ensures proper
// diags between public and package.
if (SF->isMaxAccessLevelUsingImportInternal(problematicImport.value()))
SF->registerAccessLevelUsingImport(problematicImport.value(),
AccessLevel::Public);
}
if (ctx.LangOpts.EnableModuleApiImportRemarks) {
ModuleDecl *importedVia = problematicImport->module.importedModule,
*sourceModule = ext->getModuleContext();
Expand All @@ -392,7 +437,8 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
static_cast<unsigned>(originKind))
.warnUntilSwiftVersionIf((warnIfConformanceUnavailablePreSwift6 &&
originKind != DisallowedOriginKind::SPIOnly &&
originKind != DisallowedOriginKind::NonPublicImport) ||
originKind != DisallowedOriginKind::PackageImport &&
originKind != DisallowedOriginKind::InternalOrLessImport) ||
originKind == DisallowedOriginKind::MissingImport,
6);

Expand All @@ -401,7 +447,8 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
addMissingImport(loc, ext, where);

// If limited by an import, note which one.
if (originKind == DisallowedOriginKind::NonPublicImport) {
if (originKind == DisallowedOriginKind::InternalOrLessImport ||
originKind == DisallowedOriginKind::PackageImport) {
const DeclContext *DC = where.getDeclContext();
ImportAccessLevel limitImport = ext->getImportAccessFrom(DC);
assert(limitImport.has_value() &&
Expand Down
Loading