Skip to content

Commit 1afc6af

Browse files
authored
Merge pull request swiftlang#63912 from xymus/access-level-import-parsing
[Parser|Sema] Accept access level on imports with experimental flag
2 parents a0e1810 + 38fbe1d commit 1afc6af

File tree

12 files changed

+145
-10
lines changed

12 files changed

+145
-10
lines changed

include/swift/AST/Decl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,12 @@ class ImportDecl final : public Decl,
14231423
/// the decls it references. Otherwise, returns an empty array.
14241424
ArrayRef<ValueDecl *> getDecls() const;
14251425

1426+
/// Access level of this import, either explicitly declared or implicit.
1427+
AccessLevel getAccessLevel() const;
1428+
1429+
/// Is the access level of this import implicit, aka a default import?
1430+
bool isAccessLevelImplicit() const;
1431+
14261432
const clang::Module *getClangModule() const {
14271433
return getClangNode().getClangModule();
14281434
}

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,6 +2048,16 @@ ERROR(spi_only_imports_not_enabled, none,
20482048
"'@_spiOnly' requires setting the frontend flag '-experimental-spi-only-imports'",
20492049
())
20502050

2051+
// Access level on imports
2052+
ERROR(access_level_on_import_not_enabled, none,
2053+
"Access level on imports require '-enable-experimental-feature AccessLevelOnImport'",
2054+
())
2055+
ERROR(access_level_on_import_unsupported, none,
2056+
"The access level %0 is unsupported on imports: "
2057+
"only 'public', 'package', 'internal', 'fileprivate' and 'private' "
2058+
"are unsupported",
2059+
(DeclAttribute))
2060+
20512061
// Opaque return types
20522062
ERROR(opaque_type_invalid_constraint,none,
20532063
"an 'opaque' type must specify only 'Any', 'AnyObject', protocols, "

include/swift/AST/Import.h

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -584,14 +584,24 @@ struct AttributedImport {
584584
/// attribute, this is the given access level.
585585
Optional<AccessLevel> docVisibility;
586586

587+
/// Access level limiting how imported types can be exported.
588+
AccessLevel accessLevel;
589+
590+
/// Location of the attribute that defined \c accessLevel. Also indicates
591+
/// if the access level was implicit or explicit.
592+
SourceLoc accessLevelLoc;
593+
587594
AttributedImport(ModuleInfo module, SourceLoc importLoc = SourceLoc(),
588595
ImportOptions options = ImportOptions(),
589596
StringRef filename = {}, ArrayRef<Identifier> spiGroups = {},
590597
SourceRange preconcurrencyRange = {},
591-
Optional<AccessLevel> docVisibility = None)
598+
Optional<AccessLevel> docVisibility = None,
599+
AccessLevel accessLevel = AccessLevel::Public,
600+
SourceLoc accessLevelLoc = SourceLoc())
592601
: module(module), importLoc(importLoc), options(options),
593602
sourceFileArg(filename), spiGroups(spiGroups),
594-
preconcurrencyRange(preconcurrencyRange), docVisibility(docVisibility) {
603+
preconcurrencyRange(preconcurrencyRange), docVisibility(docVisibility),
604+
accessLevel(accessLevel), accessLevelLoc(accessLevelLoc) {
595605
assert(!(options.contains(ImportFlags::Exported) &&
596606
options.contains(ImportFlags::ImplementationOnly)) ||
597607
options.contains(ImportFlags::Reserved));
@@ -601,15 +611,18 @@ struct AttributedImport {
601611
AttributedImport(ModuleInfo module, AttributedImport<OtherModuleInfo> other)
602612
: AttributedImport(module, other.importLoc, other.options,
603613
other.sourceFileArg, other.spiGroups,
604-
other.preconcurrencyRange, other.docVisibility) { }
614+
other.preconcurrencyRange, other.docVisibility,
615+
other.accessLevel, other.accessLevelLoc) { }
605616

606617
friend bool operator==(const AttributedImport<ModuleInfo> &lhs,
607618
const AttributedImport<ModuleInfo> &rhs) {
608619
return lhs.module == rhs.module &&
609620
lhs.options.toRaw() == rhs.options.toRaw() &&
610621
lhs.sourceFileArg == rhs.sourceFileArg &&
611622
lhs.spiGroups == rhs.spiGroups &&
612-
lhs.docVisibility == rhs.docVisibility;
623+
lhs.docVisibility == rhs.docVisibility &&
624+
lhs.accessLevel == rhs.accessLevel &&
625+
lhs.accessLevelLoc == rhs.accessLevelLoc;
613626
}
614627

615628
AttributedImport<ImportedModule> getLoaded(ModuleDecl *loadedModule) const {
@@ -761,14 +774,16 @@ struct DenseMapInfo<swift::AttributedImport<ModuleInfo>> {
761774
SourceLocDMI::getEmptyKey(),
762775
ImportOptionsDMI::getEmptyKey(),
763776
StringRefDMI::getEmptyKey(),
764-
{}, {}, None);
777+
{}, {}, None,
778+
swift::AccessLevel::Public, {});
765779
}
766780
static inline AttributedImport getTombstoneKey() {
767781
return AttributedImport(ModuleInfoDMI::getTombstoneKey(),
768782
SourceLocDMI::getEmptyKey(),
769783
ImportOptionsDMI::getTombstoneKey(),
770784
StringRefDMI::getTombstoneKey(),
771-
{}, {}, None);
785+
{}, {}, None,
786+
swift::AccessLevel::Public, {});
772787
}
773788
static inline unsigned getHashValue(const AttributedImport &import) {
774789
return detail::combineHashValue(
@@ -783,7 +798,9 @@ struct DenseMapInfo<swift::AttributedImport<ModuleInfo>> {
783798
ImportOptionsDMI::isEqual(a.options, b.options) &&
784799
StringRefDMI::isEqual(a.sourceFileArg, b.sourceFileArg) &&
785800
a.spiGroups == b.spiGroups &&
786-
a.docVisibility == b.docVisibility;
801+
a.docVisibility == b.docVisibility &&
802+
a.accessLevel == b.accessLevel &&
803+
a.accessLevelLoc == b.accessLevelLoc;
787804
}
788805
};
789806
}

include/swift/Basic/Features.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ EXPERIMENTAL_FEATURE(MoveOnlyClasses, true)
116116
EXPERIMENTAL_FEATURE(OneWayClosureParameters, false)
117117
EXPERIMENTAL_FEATURE(TypeWitnessSystemInference, false)
118118
EXPERIMENTAL_FEATURE(LayoutPrespecialization, true)
119+
119120
EXPERIMENTAL_FEATURE(ModuleInterfaceExportAs, true)
121+
EXPERIMENTAL_FEATURE(AccessLevelOnImport, false)
120122

121123
/// Whether to enable experimental layout string value witnesses
122124
EXPERIMENTAL_FEATURE(LayoutStringValueWitnesses, true)

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3118,6 +3118,10 @@ static bool usesFeatureModuleInterfaceExportAs(Decl *decl) {
31183118
return false;
31193119
}
31203120

3121+
static bool usesFeatureAccessLevelOnImport(Decl *decl) {
3122+
return false;
3123+
}
3124+
31213125
static bool usesFeatureNamedOpaqueTypes(Decl *decl) {
31223126
return false;
31233127
}

lib/AST/Decl.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,28 @@ ArrayRef<ValueDecl *> ImportDecl::getDecls() const {
14131413
ScopedImportLookupRequest{mutableThis}, {});
14141414
}
14151415

1416+
AccessLevel ImportDecl::getAccessLevel() const {
1417+
if (auto attr = getAttrs().getAttribute<AccessControlAttr>()) {
1418+
return attr->getAccess();
1419+
}
1420+
1421+
auto &LangOpts = getASTContext().LangOpts;
1422+
if (LangOpts.isSwiftVersionAtLeast(6) &&
1423+
LangOpts.hasFeature(Feature::AccessLevelOnImport)) {
1424+
// Tentative Swift 6 mode where the default import is internal.
1425+
return AccessLevel::Internal;
1426+
} else {
1427+
return AccessLevel::Public;
1428+
}
1429+
}
1430+
1431+
bool ImportDecl::isAccessLevelImplicit() const {
1432+
if (auto attr = getAttrs().getAttribute<AccessControlAttr>()) {
1433+
return false;
1434+
}
1435+
return true;
1436+
}
1437+
14161438
void NominalTypeDecl::setConformanceLoader(LazyMemberLoader *lazyLoader,
14171439
uint64_t contextData) {
14181440
assert(!Bits.NominalTypeDecl.HasLazyConformances &&

lib/Sema/ImportResolution.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,11 @@ UnboundImport::UnboundImport(ImportDecl *ID)
546546
if (ID->getAttrs().hasAttribute<ImplementationOnlyAttr>())
547547
import.options |= ImportFlags::ImplementationOnly;
548548

549+
import.accessLevel = ID->getAccessLevel();
550+
if (auto attr = ID->getAttrs().getAttribute<AccessControlAttr>()) {
551+
import.accessLevelLoc = attr->getLocation();
552+
}
553+
549554
if (ID->getAttrs().hasAttribute<SPIOnlyAttr>())
550555
import.options |= ImportFlags::SPIOnly;
551556

lib/Sema/TypeCheckAttr.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ void AttributeChecker::visitLazyAttr(LazyAttr *attr) {
933933
bool AttributeChecker::visitAbstractAccessControlAttr(
934934
AbstractAccessControlAttr *attr) {
935935
// Access control attr may only be used on value decls and extensions.
936-
if (!isa<ValueDecl>(D) && !isa<ExtensionDecl>(D)) {
936+
if (!isa<ValueDecl>(D) && !isa<ExtensionDecl>(D) && !isa<ImportDecl>(D)) {
937937
diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr);
938938
return true;
939939
}
@@ -959,6 +959,19 @@ bool AttributeChecker::visitAbstractAccessControlAttr(
959959
return true;
960960
}
961961

962+
if (auto importDecl = dyn_cast<ImportDecl>(D)) {
963+
if (!D->getASTContext().LangOpts.hasFeature(Feature::AccessLevelOnImport)) {
964+
diagnoseAndRemoveAttr(attr, diag::access_level_on_import_not_enabled);
965+
return true;
966+
}
967+
968+
if (attr->getAccess() == AccessLevel::Open) {
969+
diagnoseAndRemoveAttr(attr, diag::access_level_on_import_unsupported,
970+
attr);
971+
return true;
972+
}
973+
}
974+
962975
return false;
963976
}
964977

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
/// Build the libraries.
5+
// RUN: %target-swift-frontend -emit-module %t/PublicLib.swift -o %t
6+
// RUN: %target-swift-frontend -emit-module %t/PackageLib.swift -o %t
7+
// RUN: %target-swift-frontend -emit-module %t/InternalLib.swift -o %t
8+
// RUN: %target-swift-frontend -emit-module %t/FileprivateLib.swift -o %t
9+
// RUN: %target-swift-frontend -emit-module %t/PrivateLib.swift -o %t
10+
11+
/// Check flag requirement, without and with the flag.
12+
// RUN: %target-swift-frontend -typecheck %t/ClientWithoutTheFlag.swift -I %t -verify
13+
// RUN: %target-swift-frontend -typecheck %t/ClientWithoutTheFlag.swift -I %t \
14+
// RUN: -enable-experimental-feature AccessLevelOnImport
15+
16+
//--- PublicLib.swift
17+
//--- PackageLib.swift
18+
//--- InternalLib.swift
19+
//--- FileprivateLib.swift
20+
//--- PrivateLib.swift
21+
22+
//--- ClientWithoutTheFlag.swift
23+
public import PublicLib // expected-error@:1 {{Access level on imports require '-enable-experimental-feature AccessLevelOnImport'}}
24+
package import PackageLib // expected-error@:1 {{Access level on imports require '-enable-experimental-feature AccessLevelOnImport'}}
25+
internal import InternalLib // expected-error@:1 {{Access level on imports require '-enable-experimental-feature AccessLevelOnImport'}}
26+
fileprivate import FileprivateLib // expected-error@:1 {{Access level on imports require '-enable-experimental-feature AccessLevelOnImport'}}
27+
private import PrivateLib // expected-error@:1 {{Access level on imports require '-enable-experimental-feature AccessLevelOnImport'}}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
/// Build the libraries.
5+
// RUN: %target-swift-frontend -emit-module %t/PublicLib.swift -o %t
6+
// RUN: %target-swift-frontend -emit-module %t/PackageLib.swift -o %t
7+
// RUN: %target-swift-frontend -emit-module %t/InternalLib.swift -o %t
8+
// RUN: %target-swift-frontend -emit-module %t/FileprivateLib.swift -o %t
9+
// RUN: %target-swift-frontend -emit-module %t/PrivateLib.swift -o %t
10+
// RUN: %target-swift-frontend -emit-module %t/OpenLib.swift -o %t
11+
12+
/// Check that all access levels are accepted, except for 'open'.
13+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \
14+
// RUN: -enable-experimental-feature AccessLevelOnImport -verify
15+
16+
//--- PublicLib.swift
17+
//--- PackageLib.swift
18+
//--- InternalLib.swift
19+
//--- FileprivateLib.swift
20+
//--- PrivateLib.swift
21+
//--- OpenLib.swift
22+
23+
//--- Client.swift
24+
public import PublicLib
25+
package import PackageLib
26+
internal import InternalLib
27+
fileprivate import FileprivateLib
28+
private import PrivateLib
29+
open import OpenLib // expected-error {{The access level 'open' is unsupported on imports: only 'public', 'package', 'internal', 'fileprivate' and 'private' are unsupported}}

0 commit comments

Comments
 (0)